From 529f3c8b6169408761ca700423feab851412fbd1 Mon Sep 17 00:00:00 2001 From: Lena Boeckmann Date: Wed, 18 Dec 2019 20:18:50 +0100 Subject: [PATCH] pkg: add cryptoauthlib --- pkg/cryptoauthlib/Makefile | 61 +++++ pkg/cryptoauthlib/Makefile.cryptoauthlib | 3 + pkg/cryptoauthlib/Makefile.dep | 4 + pkg/cryptoauthlib/Makefile.include | 20 ++ pkg/cryptoauthlib/contrib/Makefile | 3 + pkg/cryptoauthlib/contrib/atca.c | 217 ++++++++++++++++++ pkg/cryptoauthlib/doc.txt | 57 +++++ pkg/cryptoauthlib/include/atca.h | 45 ++++ pkg/cryptoauthlib/include/atca_params.h | 79 +++++++ .../include/cryptoauthlib_test.h | 50 ++++ ...irectories-to-target_include_directo.patch | Bin 0 -> 889 bytes ...defines-and-functions-for-riot-usage.patch | Bin 0 -> 1743 bytes 12 files changed, 539 insertions(+) create mode 100644 pkg/cryptoauthlib/Makefile create mode 100644 pkg/cryptoauthlib/Makefile.cryptoauthlib create mode 100644 pkg/cryptoauthlib/Makefile.dep create mode 100644 pkg/cryptoauthlib/Makefile.include create mode 100644 pkg/cryptoauthlib/contrib/Makefile create mode 100644 pkg/cryptoauthlib/contrib/atca.c create mode 100644 pkg/cryptoauthlib/doc.txt create mode 100644 pkg/cryptoauthlib/include/atca.h create mode 100644 pkg/cryptoauthlib/include/atca_params.h create mode 100644 pkg/cryptoauthlib/include/cryptoauthlib_test.h create mode 100644 pkg/cryptoauthlib/patches/0001-change-include_directories-to-target_include_directo.patch create mode 100644 pkg/cryptoauthlib/patches/0002-add-defines-and-functions-for-riot-usage.patch diff --git a/pkg/cryptoauthlib/Makefile b/pkg/cryptoauthlib/Makefile new file mode 100644 index 0000000000..dc05d046c2 --- /dev/null +++ b/pkg/cryptoauthlib/Makefile @@ -0,0 +1,61 @@ +PKG_NAME=cryptoauthlib +PKG_URL=https://github.com/MicrochipTech/cryptoauthlib +PKG_VERSION=af8187776cd3f3faf8bed412eaf6ff7221862e19 +PKG_LICENSE=LGPL-2.1 +PKG_TEST_NAME=cryptoauthlib_test +PKG_TESTINCLDIR = $(PKG_BUILDDIR)/test + +include $(RIOTBASE)/pkg/pkg.mk + +.PHONY: all..cmake_version_supported + +CMAKE_MINIMAL_VERSION = 3.6.0 + +CFLAGS += -Wno-missing-field-initializers -Wno-unused-function +CFLAGS += -Wno-type-limits -Wno-strict-aliasing -Wno-unused-variable -DATCA_HAL_I2C +CFLAGS += -Wno-unused-parameter -Wno-sign-compare -Wno-overflow -Wno-pointer-to-int-cast + +TOOLCHAIN_FILE=$(PKG_BUILDDIR)/xcompile-toolchain.cmake + +ifneq (,$(filter $(PKG_TEST_NAME),$(USEMODULE))) + all: cryptoauth build_tests +else + all: cryptoauth +endif + +cryptoauth: $(PKG_BUILDDIR)/lib/Makefile + $(MAKE) -C $(PKG_BUILDDIR)/lib + cp $(PKG_BUILDDIR)/lib/libcryptoauth.a $(BINDIR)/$(PKG_NAME).a + +$(PKG_BUILDDIR)/lib/Makefile: $(TOOLCHAIN_FILE) + cd $(PKG_BUILDDIR)/lib && \ + cmake -DCMAKE_TOOLCHAIN_FILE=$(TOOLCHAIN_FILE) \ + -Wno-dev \ + -DBUILD_TESTS=OFF \ + -DATCA_HAL_I2C:BOOL=TRUE . + +$(TOOLCHAIN_FILE): git-download + $(RIOTTOOLS)/cmake/generate-xcompile-toolchain.sh > $(TOOLCHAIN_FILE) + +build_tests: + cp $(PKG_TESTINCLDIR)/*.c $(PKG_BUILDDIR) + cp $(PKG_TESTINCLDIR)/jwt/*.c $(PKG_BUILDDIR) + cp $(PKG_TESTINCLDIR)/tng/*.c $(PKG_BUILDDIR) + cp $(PKG_TESTINCLDIR)/atcacert/*.c $(PKG_BUILDDIR) + + echo "MODULE = $(PKG_TEST_NAME)" > $(PKG_BUILDDIR)/Makefile + echo "include \$$(RIOTBASE)/Makefile.base" >> $(PKG_BUILDDIR)/Makefile + + "$(MAKE)" -C $(PKG_BUILDDIR) + +git-download: | ..cmake_version_supported + +..cmake_version_supported: + @ # Remove '-rcX' from version as they are not well handled + $(Q)\ + CMAKE_VERSION=$$(cmake --version | sed -n '1 {s/cmake version //;s/-rc.*//;p;}'); \ + $(RIOTTOOLS)/has_minimal_version/has_minimal_version.sh "$${CMAKE_VERSION}" "$(CMAKE_MINIMAL_VERSION)" cmake + +clean:: + @rm -rf $(BINDIR)/$(PKG_NAME).a + @rm -rf $(BINDIR)/$(PKG_TEST_NAME).a diff --git a/pkg/cryptoauthlib/Makefile.cryptoauthlib b/pkg/cryptoauthlib/Makefile.cryptoauthlib new file mode 100644 index 0000000000..35f61e207b --- /dev/null +++ b/pkg/cryptoauthlib/Makefile.cryptoauthlib @@ -0,0 +1,3 @@ +MODULE = cryptoauthlib + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/cryptoauthlib/Makefile.dep b/pkg/cryptoauthlib/Makefile.dep new file mode 100644 index 0000000000..5ac1d3b493 --- /dev/null +++ b/pkg/cryptoauthlib/Makefile.dep @@ -0,0 +1,4 @@ +USEMODULE += xtimer +FEATURES_REQUIRED += periph_i2c +USEMODULE += auto_init_security +USEMODULE += cryptoauthlib_contrib diff --git a/pkg/cryptoauthlib/Makefile.include b/pkg/cryptoauthlib/Makefile.include new file mode 100644 index 0000000000..d7bc7be062 --- /dev/null +++ b/pkg/cryptoauthlib/Makefile.include @@ -0,0 +1,20 @@ +PKG_BUILDDIR ?= $(PKGDIRBASE)/cryptoauthlib +PKG_TESTINCLDIR = $(PKG_BUILDDIR)/test + +INCLUDES += -I$(PKG_BUILDDIR) +INCLUDES += -I$(PKG_BUILDDIR)/lib +INCLUDES += -I$(PKG_BUILDDIR)/app +INCLUDES += -I$(RIOTPKG)/cryptoauthlib/include + +DIRS += $(RIOTPKG)/cryptoauthlib/contrib + +ifneq (,$(filter cryptoauthlib_test,$(USEMODULE))) + INCLUDES += -I$(PKG_TESTINCLDIR) + INCLUDES += -I$(PKG_TESTINCLDIR)/jwt + INCLUDES += -I$(PKG_TESTINCLDIR)/tng + INCLUDES += -I$(PKG_TESTINCLDIR)/atcacert +endif + +ifneq (,$(filter cortex-m%,$(CPU_ARCH))) + TOOLCHAINS_BLACKLIST += llvm +endif diff --git a/pkg/cryptoauthlib/contrib/Makefile b/pkg/cryptoauthlib/contrib/Makefile new file mode 100644 index 0000000000..5c203b7715 --- /dev/null +++ b/pkg/cryptoauthlib/contrib/Makefile @@ -0,0 +1,3 @@ +MODULE = cryptoauthlib_contrib + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/cryptoauthlib/contrib/atca.c b/pkg/cryptoauthlib/contrib/atca.c new file mode 100644 index 0000000000..b64ecc3a56 --- /dev/null +++ b/pkg/cryptoauthlib/contrib/atca.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup pkg_cryptoauthlib + * @{ + * + * @file + * @brief HAL implementation for the library Microchip CryptoAuth devices + * + * @author Lena Boeckmann + * + * @} + */ +#include +#include +#include "xtimer.h" +#include "periph/i2c.h" +#include "periph/gpio.h" + +#include "atca.h" +#include "atca_params.h" + + +/* Timer functions */ +void atca_delay_us(uint32_t delay) +{ + xtimer_usleep(delay); +} + +void atca_delay_10us(uint32_t delay) +{ + xtimer_usleep(delay * 10); +} + +void atca_delay_ms(uint32_t delay) +{ + xtimer_usleep(delay * 1000); +} + +/* Hal I2C implementation */ +ATCA_STATUS hal_i2c_init(void *hal, ATCAIfaceCfg *cfg) +{ + (void)hal; + if (cfg->iface_type != ATCA_I2C_IFACE) { + return ATCA_BAD_PARAM; + } + + atcab_wakeup(); + + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_post_init(ATCAIface iface) +{ + (void)iface; + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_send(ATCAIface iface, uint8_t *txdata, int txlength) +{ + ATCAIfaceCfg *cfg = atgetifacecfg(iface); + int ret; + + /* The first byte of the command package contains the word address */ + txdata[0] = ATCA_DATA_ADDR; + + i2c_acquire(cfg->atcai2c.bus); + ret = i2c_write_bytes(cfg->atcai2c.bus, (cfg->atcai2c.slave_address >> 1), + txdata, txlength + 1, 0); + i2c_release(cfg->atcai2c.bus); + + if (ret != 0) { + return ATCA_TX_FAIL; + } + + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_receive(ATCAIface iface, uint8_t *rxdata, + uint16_t *rxlength) +{ + ATCAIfaceCfg *cfg = atgetifacecfg(iface); + uint8_t retries = cfg->rx_retries; + uint8_t length_package = 0; + uint8_t bytes_to_read; + int ret = -1; + + /* Every command needs some time to be executed. We check whether the device is done + by polling, so we don't have to wait for the max execution time. For that there's + a number of retries (specified in the device descriptor). If polling is not successful + this function returns an error code. */ + + i2c_acquire(cfg->atcai2c.bus); + while (retries-- > 0 && ret != 0) { + /* read first byte (size of output data) and store it in variable length_package + to check if output will fit into rxdata */ + ret = i2c_read_byte(cfg->atcai2c.bus, (cfg->atcai2c.slave_address >> 1), + &length_package, 0); + } + i2c_release(cfg->atcai2c.bus); + + if (ret != 0) { + return ATCA_RX_TIMEOUT; + } + + bytes_to_read = length_package - 1; + + if (bytes_to_read > *rxlength) { + return ATCA_SMALL_BUFFER; + } + + /* CRC function calculates value of the whole output package, so to get a correct + result we need to include the length of the package we got before into rxdata as first byte. */ + rxdata[0] = length_package; + + /* reset ret and retries to read the rest of the output */ + ret = -1; + retries = cfg->rx_retries; + + /* read rest of output and insert into rxdata array after first byte */ + i2c_acquire(cfg->atcai2c.bus); + while (retries-- > 0 && ret != 0) { + ret = i2c_read_bytes(cfg->atcai2c.bus, + (cfg->atcai2c.slave_address >> 1), (rxdata + 1), + bytes_to_read, 0); + } + i2c_release(cfg->atcai2c.bus); + + if (ret != 0) { + return ATCA_RX_TIMEOUT; + } + + *rxlength = length_package; + + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_wake(ATCAIface iface) +{ + ATCAIfaceCfg *cfg = atgetifacecfg(iface); + uint8_t data[4] = { 0 }; + + i2c_acquire(cfg->atcai2c.bus); + i2c_write_byte(cfg->atcai2c.bus, ATCA_WAKE_ADDR, data[0], 0); + i2c_release(cfg->atcai2c.bus); + + atca_delay_us(cfg->wake_delay); + + uint8_t retries = cfg->rx_retries; + int status = -1; + + i2c_acquire(cfg->atcai2c.bus); + while (retries-- > 0 && status != 0) { + status = i2c_read_bytes(cfg->atcai2c.bus, + (cfg->atcai2c.slave_address >> 1), + &data[0], 4, 0); + } + i2c_release(cfg->atcai2c.bus); + + if (status != ATCA_SUCCESS) { + return ATCA_COMM_FAIL; + } + + return hal_check_wake(data, 4); +} + +ATCA_STATUS hal_i2c_idle(ATCAIface iface) +{ + ATCAIfaceCfg *cfg = atgetifacecfg(iface); + + i2c_acquire(cfg->atcai2c.bus); + i2c_write_byte(cfg->atcai2c.bus, (cfg->atcai2c.slave_address >> 1), + ATCA_IDLE_ADDR, 0); + i2c_release(cfg->atcai2c.bus); + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_sleep(ATCAIface iface) +{ + ATCAIfaceCfg *cfg = atgetifacecfg(iface); + + i2c_acquire(cfg->atcai2c.bus); + i2c_write_byte(cfg->atcai2c.bus, (cfg->atcai2c.slave_address >> 1), + ATCA_SLEEP_ADDR, 0); + i2c_release(cfg->atcai2c.bus); + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_release(void *hal_data) +{ + (void)hal_data; + /* This function is unimplemented, because currently the bus gets acquired + and released before and after each read or write operation. It returns + a success code, in case it gets called somewhere in the library. */ + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_discover_buses(int i2c_buses[], int max_buses) +{ + (void)i2c_buses; + (void)max_buses; + return ATCA_UNIMPLEMENTED; +} + +ATCA_STATUS hal_i2c_discover_devices(int bus_num, ATCAIfaceCfg *cfg, int *found) +{ + (void)bus_num; + (void)cfg; + (void)found; + return ATCA_UNIMPLEMENTED; +} diff --git a/pkg/cryptoauthlib/doc.txt b/pkg/cryptoauthlib/doc.txt new file mode 100644 index 0000000000..7150eb14c0 --- /dev/null +++ b/pkg/cryptoauthlib/doc.txt @@ -0,0 +1,57 @@ +/** + * @defgroup pkg_cryptoauthlib Microchip CryptoAuthentication Library + * @ingroup pkg drivers sys_crypto + * @brief Provides the library for Microchip CryptoAuth devices + * @see https://github.com/MicrochipTech/cryptoauthlib + * + * # Introduction + * + * This package provides support for the official library for Microchip CryptoAuth devices. + * + * + * ## Warning + * + * Some functions can only be used, when the data, config and otp zones of the + * device are locked. Locking is permanent and cannot be undone. Be careful if + * you're not sure you've configured everything correctly. + * For more information we refer to the data sheet of the device. + * + * + * ## Usage + * + * Add + * USEPKG += cryptoauthlib + * to your Makefile. + * + * + * ## Implementation status + * + * This implementation was partly tested with ATECC508A and ATECC608A devices. + * We haven't tested the functions that require locking the device, yet. There's + * a wrapper in the cryptoauthlib/contrib folder, which implements most of the + * HAL functions. + * Currently the functions hal_i2c_discover_devices and hal_i2c_discover_buses + * are unimplemented, as well as hal_i2c_post_init. + * + * ### Wake function + * + * The wake function only works when a 0x00 byte is sent on an i2c interface that + * runs with with 133kHz or less. + * Currently RIOT sets the baudrate to the default value of 100 kHz and there's + * no interface to change that. If the default speed ever changes to a value + * higher than 133 kHz the wake function needs to be adapted. + * For more information on how to send a proper wake condition we refer to the + * data sheet of the device. + * + * ## Tests + * + * The library provides unittests for the library functions. There + * is a directory called "pkg_cryptoauthlib_internal_tests" in the RIOT testfolder + * which runs part of the unittests. + * Some of the provided tests can only be run when the config, data and/or otp zones + * of the device are locked. Some tests (but not all) will automatically lock zones + * as needed. We omit those tests at the moment, because zones can only be locked + * permanently. Unlocking is not possible! + * Also there is a test for comparing the runtime of the RIOT software implementation + * and the CryptoAuth hardware implementation for calculating a SHA-256 hash value. + */ diff --git a/pkg/cryptoauthlib/include/atca.h b/pkg/cryptoauthlib/include/atca.h new file mode 100644 index 0000000000..5f33d4c477 --- /dev/null +++ b/pkg/cryptoauthlib/include/atca.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup pkg_cryptoauthlib + * @{ + * + * @file + * @brief Default addresses and device descriptor for CryptoAuth devices + * + * @author Lena Boeckmann + * + */ + +#ifndef ATCA_H +#define ATCA_H + +#include "periph/i2c.h" +#include "cryptoauthlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default device and word addresses for ATECC508A + */ +#define ATCA_I2C_ADDR (0xC0) /**< Default device address is 0xC0 */ + +#define ATCA_WAKE_ADDR (0x00) /**< Word address to write 0 to device */ +#define ATCA_SLEEP_ADDR (0x01) /**< Word address to write to for sleep mode */ +#define ATCA_IDLE_ADDR (0x02) /**< Word address to write to for idle mode */ +#define ATCA_DATA_ADDR (0x03) /**< Word address to read and write to data area */ + +#ifdef __cplusplus +} +#endif + +#endif /* ATCA_H */ +/** @} */ diff --git a/pkg/cryptoauthlib/include/atca_params.h b/pkg/cryptoauthlib/include/atca_params.h new file mode 100644 index 0000000000..8dca551ce4 --- /dev/null +++ b/pkg/cryptoauthlib/include/atca_params.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup pkg_cryptoauthlib + * @{ + * + * @file + * @brief Default configuration for Microchip CryptoAuth devices + * + * @author Lena Boeckmann + * + */ + +#ifndef ATCA_PARAMS_H +#define ATCA_PARAMS_H + +#include "cryptoauthlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set default configuration parameters for the ATCA device + * + * The CryptoAuth library defines the data structure ATCAIfaceCfg for + * device initialization. We use this instead of a self defined params + * struct and store it in the params array. + * ATCAIfaceCfg contains a variable for the bus address, which is never + * used by the library. We use it to store RIOT's I2C_DEV. + * We also initialize the baud rate with zero, because RIOT doesn't have + * an API to change baud. + * + * @ingroup config + * @{ + */ + +#ifndef ATCA_PARAM_I2C +#define ATCA_PARAM_I2C I2C_DEV(0) +#endif +#ifndef ATCA_PARAM_ADDR +#define ATCA_PARAM_ADDR (ATCA_I2C_ADDR) +#endif +#ifndef ATCA_RX_RETRIES +#define ATCA_RX_RETRIES (20) +#endif + +#ifndef ATCA_PARAMS +#define ATCA_PARAMS { .iface_type = ATCA_I2C_IFACE, \ + .devtype = ATECC508A, \ + .atcai2c.slave_address = ATCA_PARAM_ADDR, \ + .atcai2c.bus = ATCA_PARAM_I2C, \ + .atcai2c.baud = -1, /**< Not used in RIOT */ \ + .wake_delay = 1500, \ + .rx_retries = ATCA_RX_RETRIES } +#endif + +/**@}*/ + +/** + * @brief Allocation of ATCA device descriptors + */ +static const ATCAIfaceCfg atca_params[] = +{ + ATCA_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ATCA_PARAMS_H */ +/** @} */ diff --git a/pkg/cryptoauthlib/include/cryptoauthlib_test.h b/pkg/cryptoauthlib/include/cryptoauthlib_test.h new file mode 100644 index 0000000000..8a66d58091 --- /dev/null +++ b/pkg/cryptoauthlib/include/cryptoauthlib_test.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup pkg_cryptoauthlib + * @{ + * + * @file + * + * @author Lena Boeckmann + * + */ + +#ifndef CRYPTOAUTHLIB_TEST_H +#define CRYPTOAUTHLIB_TEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cryptoauthlib.h" +#include "atca.h" +#include "atca_params.h" + +/** + * @brief Helper function to use the library's unittests + * This function is defined in the cryptoauth library via patch. + * It is used to pass commands to run built-in unit tests of the library. + */ +int atca_run_cmd(const char *command); + +/** + * @brief Function switches the default cfg in cryptoauthlib test to RIOT cfg + */ +void riot_switch_cfg(ATCAIfaceCfg *cfg) +{ + *cfg = atca_params[0]; +} + +#ifdef __cplusplus +} +#endif + +#endif /* CRYPTOAUTHLIB_TEST_H */ +/** @} */ diff --git a/pkg/cryptoauthlib/patches/0001-change-include_directories-to-target_include_directo.patch b/pkg/cryptoauthlib/patches/0001-change-include_directories-to-target_include_directo.patch new file mode 100644 index 0000000000000000000000000000000000000000..03d4eb1297de755840bebaef98d0448291377224 GIT binary patch literal 889 zcmcIiZHwAK5dOZuVm|bPl1tp(Xndg*H7drWIz7N1+tFN2$uHHorQIk5wvN=17IwDa8-F6vy;j@0}b!keSdp zg6$dapbzBR`tbw{o@qC>DKqm;zw^gCvH1Yb9kt^W~Nhkt{t6l#wkRdzV%nB=lH zE>$hD0bPL>^&WL{10}?^Z2||GcKoGy#85W6arBdhU%>Ao zWwkGQjt6{E?KUB~EDEsgz0@E&H)zuvMj};?Pmr=6W}fFboOuK6_Q7=Pb-R}eZ*mdK zvfw7)cnmhwu?3ak>^Q{g;erVeABXL7yK`b%Czz}gxEJG z*vjhXldi;Bm$2DQ!@!4kKmFz8(>(EaQ8Zs|lX$(0{CP49qTk>+9bL*gPfnuN&z(}{ z;v}!a1n%N#60H1iH+%JO6YJm0``@X_WoQq<%H?KUjX9ueDW|$RT`mBdHx)$d%{qn8aU}({&hwuFu#uD<$0Fu0sj=19&4BO#lD@ literal 0 HcmV?d00001 diff --git a/pkg/cryptoauthlib/patches/0002-add-defines-and-functions-for-riot-usage.patch b/pkg/cryptoauthlib/patches/0002-add-defines-and-functions-for-riot-usage.patch new file mode 100644 index 0000000000000000000000000000000000000000..d34de7392337487b250b339a63001143fd108d99 GIT binary patch literal 1743 zcmchXZExZ@5Xay9Q_Qu}9yAb=@J#nASW2~==oXcryVGhF8D~s<7aWv0-LCfbyWcoa ziSD-AcV|Ix#^&#t-;C!b&vt+~><@!rIM_(Zd)+>IelPBI`a_R*I)fpPedO`LlW?19 zSm7T0K6u`!`as}$KD!~A5iC%1c$cC0XUDaMx2SZI<@C+c@h;n_w(ly9#!dL=X2N%y zGT*u}Ua@O#a0FqA4d}z~T$5(tf!`Um`y<~2$MZdpt;)^cD9i{xE+^siJp`@b6Yw~O z7$vC|z;z6=)WWDt7a+47a+Mh<3%*6hn$0EygN12{UEJK~nZTmRa#z5=;CI1!HeTME z&{pulVGw{+DIF;iuD2LBtb)=7=GECXr%o2~D@FTWYt|Wym6XtIZj}MvdeM!|i)N;D zjE@j((ChJm>xxd-+XNDbNUzheQN5zaC?I%6b3BG-Flh9_sh;C8gH{XHbdn_rCP8{p z<_p|R<_p+yk!Mh3K)%Nr7W!8MBGWQe!oX)`lIjnW)y*Lj%Erv#ytHza0>M955`H`G%h6i;la+B`R&J-^}G<`t(XR3O|YcMj*Ol1yH$DE-){(s4I+6-|H?Y*{*|*FMVYD?~n7kC` z5R2SLJJe-F?J-U<2YNqFZAj*@sI<&338qbH)v8Z9SqyW@1ro0d0>-LBuytjVSyb1+ P&g>F(_P`C=uE*GaI(ihb literal 0 HcmV?d00001