From ce2130e8791e9dbaceed7ae13e849dd005a1c34a Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 15 Aug 2023 23:03:08 -0400 Subject: [PATCH 01/26] chore: removed byteutil.h --- packages/atchops/CMakeLists.txt | 13 +++++++++++- packages/atchops/src/byteutil.c | 21 ------------------- packages/atchops/src/rsa.c | 18 ++++++++-------- packages/atchops/tests/test_rsaencrypt.c | 11 +++++++++- .../atchops/tests/test_rsapublicpopulate.c | 1 - 5 files changed, 31 insertions(+), 33 deletions(-) delete mode 100644 packages/atchops/src/byteutil.c diff --git a/packages/atchops/CMakeLists.txt b/packages/atchops/CMakeLists.txt index 231ef5af..35aa00d2 100644 --- a/packages/atchops/CMakeLists.txt +++ b/packages/atchops/CMakeLists.txt @@ -16,7 +16,6 @@ project( set(atchops_srcs ${CMAKE_CURRENT_LIST_DIR}/src/aes_ctr.c ${CMAKE_CURRENT_LIST_DIR}/src/base64.c - ${CMAKE_CURRENT_LIST_DIR}/src/byteutil.c ${CMAKE_CURRENT_LIST_DIR}/src/rsa.c ${CMAKE_CURRENT_LIST_DIR}/src/sha.c ) @@ -111,3 +110,15 @@ if(ATCHOPS_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() + + +# Temp (DO NOT COMMIT) +add_executable(main src/main.c) +target_link_libraries(main PRIVATE atchops) +target_link_libraries(main PRIVATE MbedTLS::mbedcrypto) +target_include_directories(main PRIVATE ${atchops_include_dir}) + +install( + TARGETS main + ARCHIVE DESTINATION ${CMAKE_INSTALL_BINDIR} +) \ No newline at end of file diff --git a/packages/atchops/src/byteutil.c b/packages/atchops/src/byteutil.c deleted file mode 100644 index be74d992..00000000 --- a/packages/atchops/src/byteutil.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include "atchops/byteutil.h" - -void printx(unsigned char *data, size_t len) -{ - // TODO check len here, error handle return an int - for (size_t i = 0; i < len; i++) - { - printf("%02x ", data[i]); - } - printf("\n"); -} - -void copy(unsigned char *dst, unsigned char *src, size_t len) -{ - // TODO: check len here, error handle return an int - for (size_t i = 0; i < len; i++) - { - dst[i] = src[i]; - } -} diff --git a/packages/atchops/src/rsa.c b/packages/atchops/src/rsa.c index 53fcc732..5ee09343 100644 --- a/packages/atchops/src/rsa.c +++ b/packages/atchops/src/rsa.c @@ -8,7 +8,6 @@ #include #include "atchops/rsa.h" #include "atchops/base64.h" -#include "atchops/byteutil.h" #include "atchops/sha.h" int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t publickeybase64len, atchops_rsa_publickey *publickeystruct) @@ -88,12 +87,12 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub // } publickeystruct->n_param.len = current->buf.len; publickeystruct->n_param.value = malloc(sizeof(unsigned char) * publickeystruct->n_param.len); - copy(publickeystruct->n_param.value, current->buf.p, publickeystruct->n_param.len); + memcpy(publickeystruct->n_param.value, current->buf.p, publickeystruct->n_param.len); current = current->next; publickeystruct->e_param.len = current->buf.len; publickeystruct->e_param.value = malloc(sizeof(unsigned char) * publickeystruct->e_param.len); - copy(publickeystruct->e_param.value, current->buf.p, publickeystruct->e_param.len); + memcpy(publickeystruct->e_param.value, current->buf.p, publickeystruct->e_param.len); goto ret; @@ -205,31 +204,31 @@ int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t p // printf("n\n"); privatekeystruct->n_param.len = current->buf.len; privatekeystruct->n_param.value = malloc(sizeof(unsigned char) * privatekeystruct->n_param.len); - copy(privatekeystruct->n_param.value, current->buf.p, privatekeystruct->n_param.len); + memcpy(privatekeystruct->n_param.value, current->buf.p, privatekeystruct->n_param.len); // printf("e\n"); current = current->next; privatekeystruct->e_param.len = current->buf.len; privatekeystruct->e_param.value = malloc(sizeof(unsigned char) * privatekeystruct->e_param.len); - copy(privatekeystruct->e_param.value, current->buf.p, privatekeystruct->e_param.len); + memcpy(privatekeystruct->e_param.value, current->buf.p, privatekeystruct->e_param.len); // printf("d\n"); current = current->next; privatekeystruct->d_param.len = current->buf.len; privatekeystruct->d_param.value = malloc(sizeof(unsigned char) * privatekeystruct->d_param.len); - copy(privatekeystruct->d_param.value, current->buf.p, privatekeystruct->d_param.len); + memcpy(privatekeystruct->d_param.value, current->buf.p, privatekeystruct->d_param.len); // printf("p\n"); current = current->next; privatekeystruct->p_param.len = current->buf.len; privatekeystruct->p_param.value = malloc(sizeof(unsigned char) * privatekeystruct->p_param.len); - copy(privatekeystruct->p_param.value, current->buf.p, privatekeystruct->p_param.len); + memcpy(privatekeystruct->p_param.value, current->buf.p, privatekeystruct->p_param.len); // printf("q\n"); current = current->next; privatekeystruct->q_param.len = current->buf.len; privatekeystruct->q_param.value = malloc(sizeof(unsigned char) * privatekeystruct->q_param.len); - copy(privatekeystruct->q_param.value, current->buf.p, privatekeystruct->q_param.len); + memcpy(privatekeystruct->q_param.value, current->buf.p, privatekeystruct->q_param.len); goto ret; @@ -245,7 +244,8 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md const size_t hashlen = 32; unsigned char *hash = malloc(sizeof(char) * hashlen); - ret = atchops_sha_hash(message, messagelen, &hash, mdtype); + size_t hasholen = 0; + ret = atchops_sha_hash(hash, hashlen, &hasholen, message, messagelen, mdtype); if (ret != 0) goto ret; diff --git a/packages/atchops/tests/test_rsaencrypt.c b/packages/atchops/tests/test_rsaencrypt.c index 6b31bc99..8d374d5b 100644 --- a/packages/atchops/tests/test_rsaencrypt.c +++ b/packages/atchops/tests/test_rsaencrypt.c @@ -3,12 +3,21 @@ #include #include #include "atchops/rsa.h" -#include "atchops/byteutil.h" #define PUBLICKEYBASE64 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg3P7mefqZg2GNQPiEHYinmTYUcbbW2Ar9Wi5LCD/uRZNRiQJypbAQbpvk6fAo1wh5Ntp1kjPGHrIikUBVREItTkulobOOPVNaC5FUg86kQJ2Wk+ZyPaCIfrto7Gv+yn2DiKqjdYdexjmaKbMO90WSZ7yEmC2mq8bRQASD0PoG3RX1skhGkV1FvPbH4OEDuzMxHfGcCvCi3+BPcbgjLIT/dKe2zAHS5/fE9OK1bz+/FutJTF8M6LKQY8E+h2cQjTEn3RRJlcMp4rwq/0GNmm3mNY5EhUcamKiSWILG9a8nYzeIUafXmESCZk+J1yVu9QcmXP8Dokv+4KLv76/Y1RsqQIDAQAB" #define PLAINTEXT "banana" +static void printx(const char *str, size_t len) +{ + printf("hex: "); + for(size_t i = 0; i < len; i++) + { + printf("%02x", str[i]); + } + printf("\n"); +} + int main() { int ret = 1; diff --git a/packages/atchops/tests/test_rsapublicpopulate.c b/packages/atchops/tests/test_rsapublicpopulate.c index 95f887d6..f30a0717 100644 --- a/packages/atchops/tests/test_rsapublicpopulate.c +++ b/packages/atchops/tests/test_rsapublicpopulate.c @@ -2,7 +2,6 @@ #include #include #include "atchops/rsa.h" -#include "atchops/byteutil.h" #define PUBLIC_KEY_BASE64 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsuuD88vWQ3Zunves9w5o3pLQ+7ClKONAMftVQ9dirJt6VD0xg5DNzX+EqdgE3MumWwkR0hFrce3T3wvx9ae8kvSjlS7QJsfGk9EjYk/lhrHJbISP5/1z/owo8a6WMH+J7YF9ouqeoZaP2YlvIt/gMsocPLmLlTFMjB7+9BGqEzdPjeUBfZe+C5C2C+3F8n2Wsgz02ScWyxdlhKEM+GViYYBZONBHgcNN44i4P09IcNYG0tM/ex4WbP0D7U2g0fRCWKtu3mWoERWenpu3rK4406ZhmkyZrWYxKJleqPxDrXbyJkXtbVYiNtg1hgCovOwQRl0eWa98aEVz0sqRy6i7DQIDAQAB" From cec855199d5eab039f3e9e6162b35f83d9d2e91b Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 15 Aug 2023 23:03:17 -0400 Subject: [PATCH 02/26] feat: sha test and refactors --- packages/atchops/include/atchops/sha.h | 3 +-- packages/atchops/src/sha.c | 22 ++++++++++++-------- packages/atchops/tests/test_sha.c | 28 ++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 packages/atchops/tests/test_sha.c diff --git a/packages/atchops/include/atchops/sha.h b/packages/atchops/include/atchops/sha.h index 418f8c44..b7ca1fec 100644 --- a/packages/atchops/include/atchops/sha.h +++ b/packages/atchops/include/atchops/sha.h @@ -11,5 +11,4 @@ typedef enum { ATCHOPS_MD_RIPEMD160, } atchops_md_type; -int atchops_sha_hash(const char *input, size_t inputlen, unsigned char **output, atchops_md_type mdtype); - +int atchops_sha_hash(unsigned char *output, unsigned long outputlen, unsigned long *outputolen, const unsigned char *input, const unsigned long inputlen, atchops_md_type mdtype); \ No newline at end of file diff --git a/packages/atchops/src/sha.c b/packages/atchops/src/sha.c index c50d2bca..1cda83cd 100644 --- a/packages/atchops/src/sha.c +++ b/packages/atchops/src/sha.c @@ -1,15 +1,16 @@ +#include #include +#include #include #include "atchops/sha.h" -int atchops_sha_hash(const char *input, size_t inputlen, unsigned char **output, atchops_md_type mdtype) +int atchops_sha_hash(unsigned char *output, unsigned long outputlen, unsigned long *outputolen, const unsigned char *input, const unsigned long inputlen, atchops_md_type mdtype) { int ret = 1; mbedtls_md_context_t md_ctx; mbedtls_md_init(&md_ctx); - - mbedtls_md_type_t md_type = mdtype; // TODO dynamic + mbedtls_md_type_t md_type = mdtype; ret = mbedtls_md_setup(&md_ctx, mbedtls_md_info_from_type(md_type), 0); if (ret != 0) @@ -23,16 +24,21 @@ int atchops_sha_hash(const char *input, size_t inputlen, unsigned char **output, if (ret != 0) goto ret; - const size_t hashlen = mbedtls_md_get_size(mbedtls_md_info_from_type(md_type)); - // printf("hashlen: %lu\n", hashlen); - unsigned char *hash = malloc(sizeof(unsigned char) * hashlen); - - *output = hash; + unsigned char *hash = malloc(sizeof(unsigned char) * outputlen); + memset(hash, 0, outputlen); ret = mbedtls_md_finish(&md_ctx, hash); if (ret != 0) goto ret; + memcpy(output, hash, outputlen); + + int i = 0; + while(i < outputlen && *(hash + i++) != '\0') + { + *outputolen += 1; + } + mbedtls_md_free(&md_ctx); goto ret; diff --git a/packages/atchops/tests/test_sha.c b/packages/atchops/tests/test_sha.c new file mode 100644 index 00000000..43e13977 --- /dev/null +++ b/packages/atchops/tests/test_sha.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include "atchops/sha.h" + +int main() +{ + int ret = 1; + + const char *src = "Hello!"; + + unsigned long dstlen = 32; + unsigned char *dst = calloc(dstlen, sizeof(unsigned char)); + unsigned long dstolen; + + ret = atchops_sha_hash(dst, dstlen, &dstolen, (const unsigned char *) src, strlen(src), ATCHOPS_MD_SHA256); + if(ret != 0) + { + printf("failed | atchops_sha_hash: %d\n", ret); + goto exit; + } + + goto exit; + +exit: { + return ret; +} +} \ No newline at end of file From 1d3df52c6533c60f658181df91d06e59380ac67f Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 15 Aug 2023 23:05:16 -0400 Subject: [PATCH 03/26] chore: delete atchops.h --- packages/atchops/include/atchops/atchops.h | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 packages/atchops/include/atchops/atchops.h diff --git a/packages/atchops/include/atchops/atchops.h b/packages/atchops/include/atchops/atchops.h deleted file mode 100644 index b2ce9018..00000000 --- a/packages/atchops/include/atchops/atchops.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "aes_ctr.h" -#include "base64.h" -#include "byteutil.h" -#include "rsa.h" -#include "sha.h" From d1372816faf4ab58a26e527cf78e70cdfedfe940 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 00:17:32 -0400 Subject: [PATCH 04/26] refactor: complete atchops refactor --- packages/atchops/include/atchops/aes_ctr.h | 15 +- packages/atchops/include/atchops/base64.h | 39 ++- packages/atchops/include/atchops/byteutil.h | 9 - packages/atchops/include/atchops/rsa.h | 80 +++--- packages/atchops/include/atchops/sha.h | 2 +- packages/atchops/src/aes_ctr.c | 40 +-- packages/atchops/src/base64.c | 6 +- packages/atchops/src/main.c | 16 ++ packages/atchops/src/rsa.c | 259 ++++++++++-------- packages/atchops/src/sha.c | 14 +- packages/atchops/tests/test_aes_ctr.c | 41 ++- packages/atchops/tests/test_base64.c | 12 +- packages/atchops/tests/test_rsadecrypt.c | 35 +-- packages/atchops/tests/test_rsaencrypt.c | 12 +- .../atchops/tests/test_rsaprivatepopulate.c | 11 +- .../atchops/tests/test_rsapublicpopulate.c | 4 +- packages/atchops/tests/test_rsasign.c | 18 +- packages/atchops/tests/test_sha.c | 4 +- 18 files changed, 323 insertions(+), 294 deletions(-) delete mode 100644 packages/atchops/include/atchops/byteutil.h create mode 100644 packages/atchops/src/main.c diff --git a/packages/atchops/include/atchops/aes_ctr.h b/packages/atchops/include/atchops/aes_ctr.h index dc3d1373..ca9eb607 100644 --- a/packages/atchops/include/atchops/aes_ctr.h +++ b/packages/atchops/include/atchops/aes_ctr.h @@ -1,16 +1,9 @@ #pragma once -#include -#include -#include - -#define IV_AMOUNT_BYTES 16 - -typedef enum -{ +typedef enum AESKeySize { AES_128 = 128, // not tested - AES_192 = 192, - AES_256 = 256, // not tested + AES_192 = 192, // not tested + AES_256 = 256, } AESKeySize; int atchops_aes_ctr_encrypt( @@ -34,5 +27,5 @@ int atchops_aes_ctr_decrypt( const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, - const size_t plaintextlen, + const unsigned long plaintextlen, unsigned long *plaintextolen); diff --git a/packages/atchops/include/atchops/base64.h b/packages/atchops/include/atchops/base64.h index 5f376f54..d1a9c227 100644 --- a/packages/atchops/include/atchops/base64.h +++ b/packages/atchops/include/atchops/base64.h @@ -1,30 +1,25 @@ #pragma once -#include -#include - -#define MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION 10000 // the max length of base64 encoded text (should be more than enough) - /** - * @brief Encodes a string to base64 - * - * @param destination the result - * @param destination_len the size of the result allocated - * @param written_len the length of the result after operation - * @param src the plain text string to encode - * @param src_len the size of the plain text string to encode - * @return int `0,` if successful, 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is not correct. *olen is always updated to reflect the amount of data that has (or would have) been written. + * @brief Base64 encode some bytes + * + * @param src src bytes that you want to encode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 encoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success */ -int atchops_base64_encode(unsigned char *dst, size_t dstlen, size_t *writtenlen, const unsigned char *src, const size_t srclen); +int atchops_base64_encode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); /** - * @brief Decodes a base64 string - * - * @param dst the result - * @param dstlen the size of the result allocated + * @brief Base64 decode some bytes + * + * @param src src bytes that you want to decode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 decoded result will be + * @param dstlen the buffer length * @param writtenlen the length of the result after operation - * @param src the base64 encoded string to decode - * @param srclen the length of the base64 encoded string to decode - * @return int `0`, if successful + * @return int 0 on success */ -int atchops_base64_decode(unsigned char * dst, size_t dstlen, size_t *writtenlen, const unsigned char *src, const size_t srclen); +int atchops_base64_decode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); diff --git a/packages/atchops/include/atchops/byteutil.h b/packages/atchops/include/atchops/byteutil.h deleted file mode 100644 index dcc08718..00000000 --- a/packages/atchops/include/atchops/byteutil.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -#include - -void printx(unsigned char *data, size_t len); - -void copy(unsigned char *dst, unsigned char *src, size_t len); - diff --git a/packages/atchops/include/atchops/rsa.h b/packages/atchops/include/atchops/rsa.h index 896f90d4..bffb64b4 100644 --- a/packages/atchops/include/atchops/rsa.h +++ b/packages/atchops/include/atchops/rsa.h @@ -1,25 +1,26 @@ #pragma once -#include -#include #include "sha.h" -typedef struct { - size_t len; +typedef struct rsa_param +{ + unsigned long len; // length of the number in bytes unsigned char *value; // hex byte array of the number } rsa_param; -typedef struct { - rsa_param n_param; // modulus - rsa_param e_param; // public exponent +typedef struct atchops_rsa_publickey +{ + rsa_param n; // modulus + rsa_param e; // public exponent } atchops_rsa_publickey; -typedef struct { - rsa_param n_param; // modulus - rsa_param e_param; // public exponent - rsa_param d_param; // private exponent - rsa_param p_param; // prime 1 - rsa_param q_param; // prime 2 +typedef struct atchops_rsa_privatekey +{ + rsa_param n; // modulus + rsa_param e; // public exponent + rsa_param d; // private exponent + rsa_param p; // prime 1 + rsa_param q; // prime 2 } atchops_rsa_privatekey; /** @@ -30,7 +31,7 @@ typedef struct { * @param publickeystruct the public key struct to populate * @return int 0 on success */ -int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t publickeybase64len, atchops_rsa_publickey *publickeystruct); +int atchops_rsa_populate_publickey(const char *publickeybase64, const unsigned long publickeybase64len, atchops_rsa_publickey *publickeystruct); /** * @brief Populate a private key struct from a base64 string @@ -40,41 +41,44 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub * @param privatekeystruct the private key struct to populate * @return int 0 on success */ -int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t privatekeybase64len, atchops_rsa_privatekey *privatekeystruct); +int atchops_rsa_populate_privatekey(const char *privatekeybase64, const unsigned long privatekeybase64len, atchops_rsa_privatekey *privatekeystruct); /** - * @brief Sign a message with an RSA 2048 Private Key + * @brief Sign a message with an RSA private key * - * @param privatekeystruct the private key struct to use for signing - * @param mdtype the message digest type to use - * @param signature the signature to populate. Pass in a pointer to a pointer to an unsigned char array. The pointer will be reassigned to the newly allocated array. - * @param signaturelen the output length of the signature + * @param privatekeystruct the private key struct to use for signing, see atchops_rsa_populate_privatekey + * @param mdtype the hash type to use, see atchops_md_type, e.g. ATCHOPS_MD_SHA256 * @param message the message to sign - * @param messagelen the length of the message + * @param messagelen the length of the message, most people use strlen() to find this length + * @param signature the signature buffer to populate + * @param signaturelen the length of the signature buffer + * @param signatureolen the length of the signature buffer after signing * @return int 0 on success */ -int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, unsigned char **signature, size_t *signaturelen, const unsigned char *message, const size_t messagelen); +int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, const unsigned char *message, const unsigned long messagelen, unsigned char *signature, const unsigned long signaturelen, unsigned long *signatureolen); /** - * @brief Encrypt a string of text with an RSA 2048 Public Key - * - * @param publickeystruct the public key struct to use for encryption, must be populated using atchops_rsa_populate_publickey - * @param plaintext the plain text to encrypt - * @param plaintextlen the length of the plain text - * @param ciphertext the ciphertext to populate. Pass in a pointer to a pointer to a char array. The pointer will be reassigned to the newly allocated array. Assumption is enough space is allocated for the ciphertext. - * @param ciphertextolen the output length of the ciphertext + * @brief Encrypt bytes with an RSA public key + * + * @param publickeystruct the public key struct to use for encryption, see atchops_rsa_populate_publickey + * @param plaintext the plaintext to encrypt + * @param plaintextlen the length of the plaintext, most people use strlen() to find this length + * @param ciphertext the ciphertext buffer to populate + * @param ciphertextlen the length of the ciphertext buffer + * @param ciphertextolen the length of the ciphertext buffer after encryption * @return int 0 on success */ -int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const char *plaintext, const size_t plaintextlen, char **ciphertext, size_t *ciphertextolen); +int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned char *plaintext, const unsigned long plaintextlen, unsigned char *ciphertext, const unsigned long ciphertextlen, unsigned long *ciphertextolen); /** - * @brief Decrypt a string of text with an RSA 2048 Private Key - * - * @param privatekeystruct the private key struct to use for decryption, must be populated using atchops_rsa_populate_privatekey - * @param ciphertextbase64encoded the base64 encoded string ciphertext to decrypt - * @param ciphertextbase64encodedlen the length of the base64 encoded string - * @param plaintext the plaintext to populate. Pass in a pointer to a pointer to a char array. The pointer will be reassigned to the newly allocated array. Assumption is enough space is allocated for the plaintext. - * @param plaintextolen the output length of the plaintext + * @brief Decrypt bytes with an RSA private key + * + * @param privatekeystruct the private key struct to use for decryption, see atchops_rsa_populate_privatekey + * @param ciphertextbase64 the ciphertext to decrypt, base64 encoded + * @param ciphertextbase64len the length of the ciphertext, most people use strlen() to find this length + * @param plaintext the plaintext buffer to populate + * @param plaintextlen the length of the plaintext buffer + * @param plaintextolen the length of the plaintext buffer after decryption * @return int 0 on success */ -int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const char *ciphertextbase64encoded, const size_t ciphertextbase64encodedlen, char **plaintext, size_t *plaintextolen); +int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, const unsigned long plaintextlen, unsigned long *plaintextolen); diff --git a/packages/atchops/include/atchops/sha.h b/packages/atchops/include/atchops/sha.h index b7ca1fec..786522e5 100644 --- a/packages/atchops/include/atchops/sha.h +++ b/packages/atchops/include/atchops/sha.h @@ -11,4 +11,4 @@ typedef enum { ATCHOPS_MD_RIPEMD160, } atchops_md_type; -int atchops_sha_hash(unsigned char *output, unsigned long outputlen, unsigned long *outputolen, const unsigned char *input, const unsigned long inputlen, atchops_md_type mdtype); \ No newline at end of file +int atchops_sha_hash(atchops_md_type mdtype, const unsigned char *input, const unsigned long inputlen, unsigned char *output, unsigned long outputlen, unsigned long *outputolen); diff --git a/packages/atchops/src/aes_ctr.c b/packages/atchops/src/aes_ctr.c index 5a1a0dce..ec4d39c2 100644 --- a/packages/atchops/src/aes_ctr.c +++ b/packages/atchops/src/aes_ctr.c @@ -8,26 +8,26 @@ #define BUFFER_SIZE 65536 int atchops_aes_ctr_encrypt( - const char *keybase64, + const char *keybase64, const unsigned long keybase64len, - const AESKeySize keybits, + const AESKeySize keybits, const unsigned char *iv, const unsigned long ivlen, - const unsigned char *plaintext, // plaintext to encrypt - const unsigned long plaintextlen, - unsigned char *ciphertextbase64, // buffer to populate + const unsigned char *plaintext, // plaintext to encrypt + const unsigned long plaintextlen, + unsigned char *ciphertextbase64, // buffer to populate const unsigned long ciphertextbase64len, // the size of the buffer - unsigned long *ciphertextbase64olen // written actual length in the buffer - ) + unsigned long *ciphertextbase64olen // written actual length in the buffer +) { int ret = 1; // 1. initialize AES key - unsigned long keylen = keybits/8; + unsigned long keylen = keybits / 8; unsigned char *key = malloc(sizeof(unsigned char) * keylen); unsigned long keyolen; - ret = atchops_base64_decode(key, keylen, &keyolen, keybase64, keybase64len); + ret = atchops_base64_decode(keybase64, keybase64len, key, keylen, &keyolen); printf("atchops_base64_decode: %d\n", ret); // printf("aes_key:\n"); // for(int i = 0; i < keyolen; i++) @@ -38,7 +38,7 @@ int atchops_aes_ctr_encrypt( // 2. pad the plaintext unsigned char *plaintextpadded; // will contain the plaintext with padded trialing bytes - size_t plaintextpaddedlen; // the length of the plain text + padding (no null terminator) + size_t plaintextpaddedlen; // the length of the plain text + padding (no null terminator) const int num_pad_bytes_to_add = 16 - (plaintextlen % 16); const unsigned char padding_val = num_pad_bytes_to_add; @@ -69,6 +69,7 @@ int atchops_aes_ctr_encrypt( unsigned long ciphertextlen = BUFFER_SIZE; unsigned char *ciphertext = malloc(sizeof(unsigned char) * ciphertextlen); + memset(ciphertext, 0, ciphertextlen); unsigned long ciphertextolen = 0; unsigned long nc_off = 0; @@ -81,7 +82,8 @@ int atchops_aes_ctr_encrypt( free(stream_block); free(key); - while(*(ciphertext + ciphertextolen++) != '\0'); + while (*(ciphertext + ciphertextolen++) != '\0') + ; --ciphertextolen; // don't count the null terminator // printf("ciphertext after encryption (not base64 encoded yet): %lu\n", ciphertextolen); @@ -93,7 +95,7 @@ int atchops_aes_ctr_encrypt( // 4. base64 encode ciphertext - ret = atchops_base64_encode(ciphertextbase64, ciphertextbase64len, ciphertextbase64olen, ciphertext, ciphertextolen); + ret = atchops_base64_encode(ciphertextbase64, ciphertextbase64len, ciphertext, ciphertextlen, &ciphertextolen); printf("atchops_base64_encode: %d\n", ret); free(ciphertext); @@ -112,18 +114,17 @@ int atchops_aes_ctr_decrypt( const unsigned long ciphertextbase64len, unsigned char *plaintext, const size_t plaintextlen, - unsigned long *plaintextolen - ) + unsigned long *plaintextolen) { int ret = 1; // 1. initialize AES key - unsigned long keylen = keybits/8; + unsigned long keylen = keybits / 8; unsigned char *key = malloc(sizeof(unsigned char) * keylen); memset(key, 0, keylen); unsigned long keyolen = 0; - ret = atchops_base64_decode(key, keylen, &keyolen, keybase64, keybase64len); + ret = atchops_base64_decode(keybase64, keybase64len, key, keylen, &keyolen); // printf("atchops_base64_decode: %d\n", ret); // printf("aes_key: %lu\n", keyolen); // for(int i = 0; i < keyolen; i++) @@ -144,7 +145,7 @@ int atchops_aes_ctr_decrypt( // printf("%02x ", *(ciphertextbase64 + i)); // } // printf("\n\n"); - ret = atchops_base64_decode(ciphertext, ciphertextlen, &ciphertextolen, ciphertextbase64, ciphertextbase64len); + ret = atchops_base64_decode(ciphertextbase64, ciphertextbase64len, ciphertext, ciphertextlen, &ciphertextolen); printf("atchops_base64_decode: %d\n", ret); // printf("ciphertext decoded: %lu\n", ciphertextolen); // for(int i = 0; i < ciphertextolen; i++) @@ -179,7 +180,8 @@ int atchops_aes_ctr_decrypt( // } // printf("\n\n"); - while(*(plaintextpadded + plaintextpaddedolen++) != '\0'); + while (*(plaintextpadded + plaintextpaddedolen++) != '\0') + ; --plaintextpaddedolen; // don't count the null terminator // printf("plaintextpadded: %lu | %.*s\n", plaintextpaddedolen, plaintextpaddedolen, plaintextpadded); @@ -199,11 +201,9 @@ int atchops_aes_ctr_decrypt( unsigned char pad_val = *(plaintextpadded + (plaintextpaddedolen - 1)); // printf("pad_val byte: 0x%02x\n", pad_val); - // add null terminator for good sake *(plaintextpadded + plaintextpaddedolen - pad_val) = '\0'; - *plaintextolen = plaintextpaddedolen - pad_val; memcpy(plaintext, plaintextpadded, *plaintextolen); diff --git a/packages/atchops/src/base64.c b/packages/atchops/src/base64.c index ecdd48a8..9bb008e2 100644 --- a/packages/atchops/src/base64.c +++ b/packages/atchops/src/base64.c @@ -1,7 +1,7 @@ -#include + #include -int atchops_base64_encode(unsigned char *dst, size_t dstlen, size_t *writtenlen, const unsigned char *src, const size_t srclen) +int atchops_base64_encode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, const unsigned long dstlen, unsigned long *writtenlen) { return mbedtls_base64_encode( dst, @@ -11,7 +11,7 @@ int atchops_base64_encode(unsigned char *dst, size_t dstlen, size_t *writtenlen, srclen); } -int atchops_base64_decode(unsigned char *dst, size_t dstlen, size_t *writtenlen, const unsigned char *src, const size_t srclen) +int atchops_base64_decode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, const unsigned long dstlen, unsigned long *writtenlen) { return mbedtls_base64_decode( dst, diff --git a/packages/atchops/src/main.c b/packages/atchops/src/main.c new file mode 100644 index 00000000..ecf2e7bd --- /dev/null +++ b/packages/atchops/src/main.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include +#include "atchops/sha.h" +#include + +int main(int argc, char **argv) +{ + atchops_md_type md_type = ATCHOPS_MD_SHA256; + unsigned char a = mbedtls_md_get_size(mbedtls_md_info_from_type(md_type)); + + printf("a: %d\n", a); + return 0; +} \ No newline at end of file diff --git a/packages/atchops/src/rsa.c b/packages/atchops/src/rsa.c index 5ee09343..276739a7 100644 --- a/packages/atchops/src/rsa.c +++ b/packages/atchops/src/rsa.c @@ -10,15 +10,24 @@ #include "atchops/base64.h" #include "atchops/sha.h" +static void printx(const unsigned char *buf, const size_t len) +{ + for (int i = 0; i < len; i++) + { + printf("%02x ", *(buf + i)); + } + printf("\n"); +} + int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t publickeybase64len, atchops_rsa_publickey *publickeystruct) { int ret = 0; // 1. base64 decode the key - size_t dstlen = MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION; + size_t dstlen = 8192; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); - size_t *writtenlen = malloc(sizeof(size_t)); - ret = atchops_base64_decode(dst, dstlen, writtenlen, publickeybase64, publickeybase64len); + size_t writtenlen = 0; + ret = atchops_base64_decode(publickeybase64, publickeybase64len, dst, dstlen, &writtenlen); if (ret != 0) goto ret; @@ -26,7 +35,7 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub // printf("writtenlen: %lu\n", *writtenlen); // printx(dst, *writtenlen); - const unsigned char *end = dst + (*writtenlen); + const unsigned char *end = dst + (writtenlen); size_t *lengthread = malloc(sizeof(size_t)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); @@ -85,31 +94,32 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub // printf("\n"); // current = current->next; // } - publickeystruct->n_param.len = current->buf.len; - publickeystruct->n_param.value = malloc(sizeof(unsigned char) * publickeystruct->n_param.len); - memcpy(publickeystruct->n_param.value, current->buf.p, publickeystruct->n_param.len); + publickeystruct->n.len = current->buf.len; + publickeystruct->n.value = malloc(sizeof(unsigned char) * publickeystruct->n.len); + memcpy(publickeystruct->n.value, current->buf.p, publickeystruct->n.len); current = current->next; - publickeystruct->e_param.len = current->buf.len; - publickeystruct->e_param.value = malloc(sizeof(unsigned char) * publickeystruct->e_param.len); - memcpy(publickeystruct->e_param.value, current->buf.p, publickeystruct->e_param.len); + publickeystruct->e.len = current->buf.len; + publickeystruct->e.value = malloc(sizeof(unsigned char) * publickeystruct->e.len); + memcpy(publickeystruct->e.value, current->buf.p, publickeystruct->e.len); goto ret; - ret: - { - return ret; - } +ret: +{ + return ret; +} } int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t privatekeybase64len, atchops_rsa_privatekey *privatekeystruct) { int ret = 1; - size_t dstlen = MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION; + size_t dstlen = 8196; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); - size_t *writtenlen = malloc(sizeof(size_t)); - ret = atchops_base64_decode(dst, dstlen, writtenlen, privatekeybase64, privatekeybase64len); + memset(dst, 0, dstlen); + size_t writtenlen = 0; + ret = atchops_base64_decode(privatekeybase64, privatekeybase64len, dst, dstlen, &writtenlen); if (ret != 0) goto ret; @@ -118,7 +128,7 @@ int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t p // printf("writtenlen: %lu\n", *writtenlen); // printf("\n"); - char *end = dst + (*writtenlen); + char *end = dst + writtenlen; // printf("1st get tag\n"); size_t *lengthread = malloc(sizeof(size_t)); @@ -202,50 +212,51 @@ int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t p current = current->next; // printf("n\n"); - privatekeystruct->n_param.len = current->buf.len; - privatekeystruct->n_param.value = malloc(sizeof(unsigned char) * privatekeystruct->n_param.len); - memcpy(privatekeystruct->n_param.value, current->buf.p, privatekeystruct->n_param.len); + privatekeystruct->n.len = current->buf.len; + privatekeystruct->n.value = malloc(sizeof(unsigned char) * privatekeystruct->n.len); + memcpy(privatekeystruct->n.value, current->buf.p, privatekeystruct->n.len); // printf("e\n"); current = current->next; - privatekeystruct->e_param.len = current->buf.len; - privatekeystruct->e_param.value = malloc(sizeof(unsigned char) * privatekeystruct->e_param.len); - memcpy(privatekeystruct->e_param.value, current->buf.p, privatekeystruct->e_param.len); + privatekeystruct->e.len = current->buf.len; + privatekeystruct->e.value = malloc(sizeof(unsigned char) * privatekeystruct->e.len); + memcpy(privatekeystruct->e.value, current->buf.p, privatekeystruct->e.len); // printf("d\n"); current = current->next; - privatekeystruct->d_param.len = current->buf.len; - privatekeystruct->d_param.value = malloc(sizeof(unsigned char) * privatekeystruct->d_param.len); - memcpy(privatekeystruct->d_param.value, current->buf.p, privatekeystruct->d_param.len); + privatekeystruct->d.len = current->buf.len; + privatekeystruct->d.value = malloc(sizeof(unsigned char) * privatekeystruct->d.len); + memcpy(privatekeystruct->d.value, current->buf.p, privatekeystruct->d.len); // printf("p\n"); current = current->next; - privatekeystruct->p_param.len = current->buf.len; - privatekeystruct->p_param.value = malloc(sizeof(unsigned char) * privatekeystruct->p_param.len); - memcpy(privatekeystruct->p_param.value, current->buf.p, privatekeystruct->p_param.len); + privatekeystruct->p.len = current->buf.len; + privatekeystruct->p.value = malloc(sizeof(unsigned char) * privatekeystruct->p.len); + memcpy(privatekeystruct->p.value, current->buf.p, privatekeystruct->p.len); // printf("q\n"); current = current->next; - privatekeystruct->q_param.len = current->buf.len; - privatekeystruct->q_param.value = malloc(sizeof(unsigned char) * privatekeystruct->q_param.len); - memcpy(privatekeystruct->q_param.value, current->buf.p, privatekeystruct->q_param.len); + privatekeystruct->q.len = current->buf.len; + privatekeystruct->q.value = malloc(sizeof(unsigned char) * privatekeystruct->q.len); + memcpy(privatekeystruct->q.value, current->buf.p, privatekeystruct->q.len); goto ret; - ret: - { - return ret; - } +ret: +{ + return ret; +} } -int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, unsigned char **signature, size_t *signaturelen, const unsigned char *message, const size_t messagelen) +int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, const unsigned char *message, const unsigned long messagelen, unsigned char *signature, const unsigned long signaturelen, unsigned long *signatureolen) { int ret = 1; // error, until successful. const size_t hashlen = 32; unsigned char *hash = malloc(sizeof(char) * hashlen); + memset(hash, 0, hashlen); size_t hasholen = 0; - ret = atchops_sha_hash(hash, hashlen, &hasholen, message, messagelen, mdtype); + ret = atchops_sha_hash(mdtype, message, messagelen, hash, hashlen, &hasholen); if (ret != 0) goto ret; @@ -261,23 +272,23 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md mbedtls_rsa_context rsa; mbedtls_rsa_init(&rsa); - // printf("\nn: %lu\n", privatekeystruct->n->len); - // printx(privatekeystruct->n->n, privatekeystruct->n->len); - // printf("\ne: %lu\n", privatekeystruct->e->len); - // printx(privatekeystruct->e->e, privatekeystruct->e->len); - // printf("\nd: %lu\n", privatekeystruct->d->len); - // printx(privatekeystruct->d->d, privatekeystruct->d->len); - // printf("\np: %lu\n", privatekeystruct->p->len); - // printx(privatekeystruct->p->p, privatekeystruct->p->len); - // printf("\nq: %lu\n", privatekeystruct->q->len); - // printx(privatekeystruct->q->q, privatekeystruct->q->len); + // printf("\nn: %lu\n", privatekeystruct.n.len); + // printx(privatekeystruct.n.value, privatekeystruct.n.len); + // printf("\ne: %lu\n", privatekeystruct.e.len); + // printx(privatekeystruct.e.value, privatekeystruct.e.len); + // printf("\nd: %lu\n", privatekeystruct.d.len); + // printx(privatekeystruct.d.value, privatekeystruct.d.len); + // printf("\np: %lu\n", privatekeystruct.p.len); + // printx(privatekeystruct.p.value, privatekeystruct.p.len); + // printf("\nq: %lu\n", privatekeystruct.q.len); + // printx(privatekeystruct.q.value, privatekeystruct.q.len); ret = mbedtls_rsa_import_raw(&rsa, - privatekeystruct.n_param.value, privatekeystruct.n_param.len, - privatekeystruct.p_param.value, privatekeystruct.p_param.len, - privatekeystruct.q_param.value, privatekeystruct.q_param.len, - privatekeystruct.d_param.value, privatekeystruct.d_param.len, - privatekeystruct.e_param.value, privatekeystruct.e_param.len); + privatekeystruct.n.value, privatekeystruct.n.len, + privatekeystruct.p.value, privatekeystruct.p.len, + privatekeystruct.q.value, privatekeystruct.q.len, + privatekeystruct.d.value, privatekeystruct.d.len, + privatekeystruct.e.value, privatekeystruct.e.len); // printf("rsa import: %d\n", ret); if (ret != 0) @@ -308,8 +319,9 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md // printf("hashlen: %lu\n", hashlen); // printf("hash: "); // printx(hash, hashlen); - int buflen = 256; - unsigned char *buf = malloc(sizeof(unsigned char) * buflen + 1); // +1 for null terminator + int buflen = 256+1; // +1 for null terminator + unsigned char *buf = malloc(sizeof(unsigned char) * buflen); + memset(buf, 0, buflen); ret = mbedtls_rsa_pkcs1_sign(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg_ctx, MBEDTLS_MD_SHA256, hashlen, hash, buf); // printf("mbedtls_rsa_pkcs1_sign: %d\n", ret); if (ret != 0) @@ -323,28 +335,35 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md // printf("\n"); // base64 encode - size_t *writtenlen = malloc(sizeof(size_t)); - unsigned char *dst = malloc(sizeof(unsigned char) * MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION); - ret = atchops_base64_encode(dst, MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION, writtenlen, buf, buflen); + unsigned long dstlen = 4096; + unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); + memset(dst, 0, dstlen); + size_t writtenlen = 0; + ret = atchops_base64_encode(buf, 256, dst, dstlen, &writtenlen); + printf("atchops_base64_encode: %d\n", ret); if (ret != 0) goto ret; // append null terminator - *(dst + *writtenlen) = '\0'; + if (writtenlen < dstlen) + { + *(dst + writtenlen) = '\0'; + } + --writtenlen; // printf("Challenge: %s\n", dst); - *signature = dst; - *signaturelen = *writtenlen; + memcpy(signature, dst, writtenlen); + *signatureolen = writtenlen; goto ret; - ret: - { - return ret; - } +ret: +{ + return ret; +} } -int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const char *plaintext, const size_t plaintextlen, char **ciphertext, size_t *ciphertextolen) +int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned char *plaintext, const unsigned long plaintextlen, unsigned char *ciphertext, const unsigned long ciphertextlen, unsigned long *ciphertextolen) { int ret = 1; @@ -352,8 +371,8 @@ int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const char *plain mbedtls_rsa_init(&rsa); // printf("importing raw...\n"); - ret = mbedtls_rsa_import_raw(&rsa, publickeystruct.n_param.value, publickeystruct.n_param.len, NULL, NULL, NULL, NULL, NULL, NULL, publickeystruct.e_param.value, publickeystruct.e_param.len); - if(ret != 0) + ret = mbedtls_rsa_import_raw(&rsa, publickeystruct.n.value, publickeystruct.n.len, NULL, NULL, NULL, NULL, NULL, NULL, publickeystruct.e.value, publickeystruct.e.len); + if (ret != 0) goto ret; // printf("n: %d\n", publickeystruct.n_param.len); @@ -362,25 +381,25 @@ int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const char *plain // printf("e: %d\n", publickeystruct.e_param.len); // printx(publickeystruct.e_param.value, publickeystruct.e_param.len); - // printf("checking public key...\n"); ret = mbedtls_rsa_check_pubkey(&rsa); // printf("public key check: %d\n", ret); - if(ret != 0) + if (ret != 0) goto ret; // printf("completing rsa...\n"); ret = mbedtls_rsa_complete(&rsa); - if(ret != 0) + if (ret != 0) goto ret; // printf("base64 encoding...\n"); // base64 encode the plain text - size_t dstlen = MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION; + size_t dstlen = 2048; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); - size_t *olen = malloc(sizeof(size_t)); - ret = atchops_base64_encode(dst, dstlen, olen, plaintext, plaintextlen); - if(ret != 0) + size_t olen = 0; + ret = atchops_base64_encode(plaintext, plaintextlen, dst, dstlen, &olen); + // printf("atchops_base64_encode_1: %d\n", ret); + if (ret != 0) goto ret; mbedtls_entropy_context entropy_ctx; @@ -398,43 +417,53 @@ int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const char *plain // encrypt the base64 encoded text size_t outputlen = 256; // 256 bytes is the result of an RSA unsigned char *output = malloc(sizeof(unsigned char) * outputlen); + memset(output, 0, outputlen); ret = mbedtls_rsa_pkcs1_encrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg_ctx, plaintextlen, plaintext, output); - if(ret != 0) + if (ret != 0) goto ret; - dstlen = MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION; + dstlen = 4096; unsigned char *dst2 = malloc(sizeof(unsigned char) * dstlen); - free(olen); - *olen = malloc(sizeof(size_t)); + olen = 0; - ret = atchops_base64_encode(dst2, dstlen, olen, output, outputlen); - if(ret != 0) + ret = atchops_base64_encode(output, outputlen, dst2, dstlen, &olen); + if (ret != 0) goto ret; // printx(dst2, *olen); // printf("%s\n", dst2); - *ciphertext = dst2; - *ciphertextolen = *olen; + memcpy(ciphertext, dst2, olen); + *ciphertextolen = olen; goto ret; - ret: - { - return ret; - } - +ret: +{ + return ret; +} } -int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const char *ciphertextbase64encoded, const size_t ciphertextbase64encodedlen, char **plaintext, size_t *plaintextolen) +int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, const unsigned long plaintextlen, unsigned long *plaintextolen) { int ret = 1; mbedtls_rsa_context rsa; mbedtls_rsa_init(&rsa); - ret = mbedtls_rsa_import_raw(&rsa, privatekeystruct.n_param.value, privatekeystruct.n_param.len, privatekeystruct.p_param.value, privatekeystruct.p_param.len, privatekeystruct.q_param.value, privatekeystruct.q_param.len, privatekeystruct.d_param.value, privatekeystruct.d_param.len, privatekeystruct.e_param.value, privatekeystruct.e_param.len); - if(ret != 0) + ret = mbedtls_rsa_import_raw( + &rsa, + privatekeystruct.n.value, + privatekeystruct.n.len, + privatekeystruct.p.value, + privatekeystruct.p.len, + privatekeystruct.q.value, + privatekeystruct.q.len, + privatekeystruct.d.value, + privatekeystruct.d.len, + privatekeystruct.e.value, + privatekeystruct.e.len); + if (ret != 0) goto ret; // printf("n: %lu\n", privatekeystruct.n_param.len); @@ -449,20 +478,21 @@ int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const char *cip // printx(privatekeystruct.q_param.value, privatekeystruct.q_param.len); ret = mbedtls_rsa_complete(&rsa); - if(ret != 0) + if (ret != 0) goto ret; ret = mbedtls_rsa_check_privkey(&rsa); - if(ret != 0) + if (ret != 0) goto ret; - // base64 decode the ciphertext - const size_t dstlen = MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION; + const size_t dstlen = 8192; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); - size_t *writtenlen = malloc(sizeof(size_t)); - ret = atchops_base64_decode(dst, dstlen, writtenlen, ciphertextbase64encoded, ciphertextbase64encodedlen); - if(ret != 0) + memset(dst, 0, dstlen); + size_t writtenlen = 0; + ret = atchops_base64_decode(ciphertextbase64, ciphertextbase64len, dst, dstlen, &writtenlen); + // printf("atchops_base64_decode: %d\n", ret); + if (ret != 0) goto ret; mbedtls_entropy_context entropy_ctx; @@ -475,28 +505,23 @@ int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const char *cip goto ret; // rsa decrypt dst - const size_t outputmaxlen = 5000; + const size_t outputmaxlen = 4096; unsigned char *output = malloc(sizeof(unsigned char) * outputmaxlen); - size_t *writtenlen2 = malloc(sizeof(size_t)); - ret = mbedtls_rsa_pkcs1_decrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg_ctx, writtenlen2, dst, output, outputmaxlen); - if(ret != 0) + memset(output, 0, outputmaxlen); + size_t writtenlen2 = 0; + ret = mbedtls_rsa_pkcs1_decrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg_ctx, &writtenlen2, dst, output, outputmaxlen); + // printf("mbedtls_rsa_pkcs1_decrypt: %d\n", ret); + if (ret != 0) goto ret; - // base64 decode the output - // const size_t dstlen2 = MAX_TEXT_LENGTH_FORBASE64_ENCODING_OPERATION; - // unsigned char *dst2 = malloc(sizeof(unsigned char) * dstlen2); - // size_t *writtenlen3 = malloc(sizeof(size_t)); - // printf("7\n"); - // ret = atchops_base64_encode(dst2, dstlen2, writtenlen3, output, *writtenlen); - // if(ret != 0) - // goto ret; - - *plaintext = output; - *plaintextolen = *writtenlen2; + memset(plaintext, 0, plaintextlen); + memcpy(plaintext, output, writtenlen2); + *plaintextolen = writtenlen2; goto ret; - ret: { - return ret; - } +ret: +{ + return ret; +} } diff --git a/packages/atchops/src/sha.c b/packages/atchops/src/sha.c index 1cda83cd..19f36c28 100644 --- a/packages/atchops/src/sha.c +++ b/packages/atchops/src/sha.c @@ -4,7 +4,7 @@ #include #include "atchops/sha.h" -int atchops_sha_hash(unsigned char *output, unsigned long outputlen, unsigned long *outputolen, const unsigned char *input, const unsigned long inputlen, atchops_md_type mdtype) +int atchops_sha_hash(atchops_md_type mdtype, const unsigned char *input, const unsigned long inputlen, unsigned char *output, unsigned long outputlen, unsigned long *outputolen) { int ret = 1; @@ -32,17 +32,19 @@ int atchops_sha_hash(unsigned char *output, unsigned long outputlen, unsigned lo goto ret; memcpy(output, hash, outputlen); - + int i = 0; - while(i < outputlen && *(hash + i++) != '\0') + while (i < outputlen && *(hash + i++) != '\0') { *outputolen += 1; } + --(*outputolen); // remove the '\0' at the end of the string mbedtls_md_free(&md_ctx); goto ret; - ret: { - return ret; - } +ret: +{ + return ret; +} } diff --git a/packages/atchops/tests/test_aes_ctr.c b/packages/atchops/tests/test_aes_ctr.c index 4bf154ae..89023210 100644 --- a/packages/atchops/tests/test_aes_ctr.c +++ b/packages/atchops/tests/test_aes_ctr.c @@ -1,27 +1,18 @@ #include #include #include -#include -#include -#include "atchops/byteutil.h" #include "atchops/aes_ctr.h" -// void printx(const unsigned char *str, size_t len) -// { -// for (int i = 0; i < len; i++) -// { -// printf("%02x ", *(str + i)); -// } -// printf("\n"); -// } +#define PLAINTEXT "I like to eat pizza 123" +#define AES_KEY "1DPU9OP3CYvamnVBMwGgL7fm8yB1klAap0Uc5Z9R79g=" int main(int argc, char **argv) { - const char *aes_key = "1DPU9OP3CYvamnVBMwGgL7fm8yB1klAap0Uc5Z9R79g="; // 32 byte key == 256 bits - const char *plaintext = "i like to eat pizza 123"; - const size_t plaintextlen = strlen(plaintext); + const unsigned char *aes_key = AES_KEY; // 32 byte key == 256 bits + const unsigned char *plaintext = PLAINTEXT; + const unsigned long plaintextlen = strlen(plaintext); + unsigned long olen = 0; - size_t olen = 0; // printf("plaintext: "); // printf("%s\n", plaintext); // printx((const unsigned char *)plaintext, strlen(plaintext)); @@ -29,41 +20,41 @@ int main(int argc, char **argv) int ret = 1; - size_t ivlen = 16; + unsigned long ivlen = 16; const unsigned char *iv = malloc(sizeof(unsigned char) * ivlen); memset(iv, 0, ivlen); - size_t ciphertextlen = 1024; + unsigned long ciphertextlen = 4096; unsigned char *ciphertext = malloc(sizeof(unsigned char) * ciphertextlen); - ret = atchops_aes_ctr_encrypt(aes_key, strlen(aes_key), AES_256, iv, ivlen, (const unsigned char *)plaintext, plaintextlen, ciphertext, ciphertextlen, &olen); + ret = atchops_aes_ctr_encrypt(aes_key, strlen(aes_key), AES_256, iv, ivlen, plaintext, plaintextlen, ciphertext, ciphertextlen, &olen); // printf("encrypted: "); // printf("%s\n", ciphertext); // printx((const unsigned char *)ciphertext, olen); // printf("encrypted-len: %lu\n", olen); if (ret != 0) { - goto ret; + goto exit; } - size_t plaintextlen2 = 1024; + size_t plaintextlen2 = 4096; unsigned char *plaintext2 = malloc(sizeof(unsigned char) * plaintextlen2); memset(iv, 0, 16); - ret = atchops_aes_ctr_decrypt(aes_key, strlen(aes_key), AES_256, iv, ivlen, (const unsigned char *)ciphertext, ciphertextlen, plaintext2, plaintextlen2, &olen); + ret = atchops_aes_ctr_decrypt(aes_key, strlen(aes_key), AES_256, iv, ivlen, ciphertext, ciphertextlen, plaintext2, plaintextlen2, &olen); // printf("decrypted: "); // printf("%s\n", plaintext2); // printx((const unsigned char *) plaintext2, olen); // printf("decrypted-len: %lu\n", olen); if (ret != 0) { - goto ret; + goto exit; } free(ciphertext); free(plaintext2); - goto ret; - -ret: + goto exit; + +exit: { return ret; } diff --git a/packages/atchops/tests/test_base64.c b/packages/atchops/tests/test_base64.c index 6f2570cc..248ca85a 100644 --- a/packages/atchops/tests/test_base64.c +++ b/packages/atchops/tests/test_base64.c @@ -12,24 +12,24 @@ int main() int retval; const unsigned char *src = SRC_STRING; - const size_t srclen = strlen(src); + const unsigned long srclen = strlen(src); - size_t dstlen = DST_BYTES_ALLOCATED; + unsigned long dstlen = DST_BYTES_ALLOCATED; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); - size_t dstlen2 = DST_BYTES_ALLOCATED; + unsigned long dstlen2 = DST_BYTES_ALLOCATED; unsigned char *dst2 = malloc(sizeof(unsigned char) * dstlen); - size_t *olen = malloc(sizeof(size_t)); + unsigned long *olen = malloc(sizeof(unsigned long)); - retval = atchops_base64_decode(dst, dstlen, olen, src, srclen); + retval = atchops_base64_decode(src, srclen, dst, dstlen, olen); if(retval) { goto ret; } // printf("olen: %lu\n", *olen); - retval = atchops_base64_encode(dst2, dstlen2, olen, dst, *olen); + retval = atchops_base64_encode(dst, *olen, dst2, dstlen2, olen); if(retval) { goto ret; diff --git a/packages/atchops/tests/test_rsadecrypt.c b/packages/atchops/tests/test_rsadecrypt.c index 6baed63a..0164d27f 100644 --- a/packages/atchops/tests/test_rsadecrypt.c +++ b/packages/atchops/tests/test_rsadecrypt.c @@ -1,6 +1,6 @@ #include -#include #include +#include #include "atchops/rsa.h" #define PRIVATEKEYBASE64 "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCDc/uZ5+pmDYY1A+IQdiKeZNhRxttbYCv1aLksIP+5Fk1GJAnKlsBBum+Tp8CjXCHk22nWSM8YesiKRQFVEQi1OS6Whs449U1oLkVSDzqRAnZaT5nI9oIh+u2jsa/7KfYOIqqN1h17GOZopsw73RZJnvISYLaarxtFABIPQ+gbdFfWySEaRXUW89sfg4QO7MzEd8ZwK8KLf4E9xuCMshP90p7bMAdLn98T04rVvP78W60lMXwzospBjwT6HZxCNMSfdFEmVwynivCr/QY2abeY1jkSFRxqYqJJYgsb1rydjN4hRp9eYRIJmT4nXJW71ByZc/wOiS/7gou/vr9jVGypAgMBAAECggEAFTkjlQypfoKOeX6///Ji0nnrpwBZKB6V2lBnHSXSw7pDDaEB57CBJ9uG6ir6YiWc30tBgjRNI2GngRN1DJvscP3jdLAdGXsZXUmjLYWB6imgnCIf7R9HkV7nATfN9tomfM/CA5ZfOiGiCaFsdfnTAF3mLWtp7/13hKNnRwmqrsvUzJrqzNFqZty7YRtkm2pxxetsycq2WGKkOjwEyJdDZSI5XbQyAzuu878WEi+Th4EI2MV21jAuP7WEAZC/tBSLLFSZgiHnMaMnQ9ogWnxCbdIJfWPBiK54kcDRN2o/I7iXAVD5W3oadEYgEn6SBuG4ZQtqTM8cFSofxrGuVfyQgQKBgQDZnwC501pza1Jm4/H/lvB67kup9EFwQFbCTjJKuHd1NuV1O0LcHER5wfgLOac2My7kXva8gsheLHdi9ciPNtqhHrJVpl4QyXYd6h7m3FImGCfd1xwqrfWPPA0aMM8e8QZGANrX7Z20xzEzSgMu/0trg7WS4P7ChO2VHXviEn4mMQKBgQCaorqBjvajf9wkwh5V06HmpaI8Ji7JvRD44l1G0eGJgCrSYbvFUQr4KzfaQltfwaMJnqoNVrqey9yhPaFacjI2QbBM3zECjmasy9m20VS70QsV7XKq7tKco8M+YfUwzOFOKrpxUm/AG7SoTmTIwsg+VkZNTPWQB74kwKmRkkj3+QKBgGxnXi8y702rWmLSjYvqHmS+K4a/m5FVG2KzHS5HcYo8DFU3bfjDRAD69Jpy366KFIPCIlqJM1JmCBqNoJhmlMXJysALnbPzBxmjtDz/5xP+2G0TaH6CJV5yZXx0b9hT6/IXHuyM+xBAYWvRJIDWvzURaPN/jKhNGyQ6ial12M0hAoGAdFadbr+6O0QEwfrxi6zPD5HpvssTRF/kFvtnJdLdle9BSEqTVF4mnJMXUDPAPwiVurUORz7K5JGHih+t9zgXIs7E7vC0FLJB+RrczzgqQauCZZrhPEy1U3e5eoOETpS1pXNsFbnprWSqxD1GgexZbtzFw350+Ul5+nigmo6uKzECgYBuZVdUFt0XpIWZ8hugSMhCxHhNZMrOAIbQp0eXMs3U25zd8RpUx4qXK39Sp+X6Ifs8jCfSjThhI5Ip0L0/vz5IyBrTUwbaCJVmLZU69UHNm2g8I6FoMQx3w7/ILo2mlfhd9QwTEzyT9dsdXq23HJeMkWzoVLieTHySu20n+PxXbQ==" @@ -10,36 +10,39 @@ int main() { int ret = 1; - atchops_rsa_privatekey privatekeystruct; - const size_t privatekeylen = strlen(PRIVATEKEYBASE64); - const char *privatekey = PRIVATEKEYBASE64; + const char *privatekeybase64 = PRIVATEKEYBASE64; + const size_t privatekeybase64len = strlen(privatekeybase64); - const size_t ciphertextlen = strlen(CIPHERTEXTBASE64); const char *ciphertext = CIPHERTEXTBASE64; + const size_t ciphertextlen = strlen(ciphertext); - ret = atchops_rsa_populate_privatekey(privatekey, privatekeylen, &privatekeystruct); + atchops_rsa_privatekey privatekeystruct; + + printf("1\n"); + ret = atchops_rsa_populate_privatekey(privatekeybase64, privatekeybase64len, &privatekeystruct); printf("atchops_rsa_populate_privatekey: %d\n", ret); - if(ret != 0) + if (ret != 0) goto ret; - char *plaintext = malloc(sizeof(unsigned char) * 5000); - size_t *plaintextlen = malloc(sizeof(size_t)); - plaintext[*plaintextlen] = '\0'; // add null terminator, not necessary but it's okay + unsigned long plaintextlen = 8192; + unsigned char *plaintext = malloc(sizeof(unsigned char) * plaintextlen); + memset(plaintext, 0, plaintextlen); + unsigned long plaintextolen = 0; - ret = atchops_rsa_decrypt(privatekeystruct, ciphertext, ciphertextlen, &plaintext, plaintextlen); + ret = atchops_rsa_decrypt(privatekeystruct, ciphertext, ciphertextlen, plaintext, plaintextlen, &plaintextolen); printf("atchops_rsa_decrypt: %d\n", ret); - if(ret != 0) + if (ret != 0) goto ret; printf("plaintext: %s\n", plaintext); free(plaintext); - free(plaintextlen); goto ret; - ret: { - return ret; - } +ret: +{ + return ret; +} } \ No newline at end of file diff --git a/packages/atchops/tests/test_rsaencrypt.c b/packages/atchops/tests/test_rsaencrypt.c index 8d374d5b..9111f565 100644 --- a/packages/atchops/tests/test_rsaencrypt.c +++ b/packages/atchops/tests/test_rsaencrypt.c @@ -36,19 +36,19 @@ int main() if(ret != 0) goto ret; - const size_t ciphertextlen = 1024; - char *ciphertext = malloc((sizeof(unsigned char) * ciphertextlen) + 1); - size_t *ciphertextolen = malloc(sizeof(size_t)); - ciphertext[*ciphertextolen] = '\0'; // add null terminator, not necessary but it's okay + const unsigned long ciphertextlen = 1024; + unsigned char *ciphertext = calloc(ciphertextlen, sizeof(unsigned char)); + memset(ciphertext, 0, ciphertextlen); + unsigned long ciphertextolen = 0; printf("encrypting...\n"); - ret = atchops_rsa_encrypt(publickeystruct, plaintext, plaintextlen, &ciphertext, ciphertextolen); + ret = atchops_rsa_encrypt(publickeystruct, plaintext, plaintextlen, ciphertext, ciphertextlen, &ciphertextolen); printf("atchops_rsa_encrypt returned: %d\n", ret); if(ret != 0) goto ret; printf("ciphertext (base64 encoded): %s\n", ciphertext); - printx(ciphertext, *ciphertextolen); + printx(ciphertext, ciphertextolen); goto ret; diff --git a/packages/atchops/tests/test_rsaprivatepopulate.c b/packages/atchops/tests/test_rsaprivatepopulate.c index fe20ad56..c0a15656 100644 --- a/packages/atchops/tests/test_rsaprivatepopulate.c +++ b/packages/atchops/tests/test_rsaprivatepopulate.c @@ -1,5 +1,4 @@ #include "atchops/rsa.h" -#include "atchops/byteutil.h" #include #include #include @@ -20,31 +19,31 @@ int main() goto ret; } - if (privatekeystruct.n_param.len <= 0) + if (privatekeystruct.n.len <= 0) { ret = 1; goto ret; } - if (privatekeystruct.e_param.len <= 0) + if (privatekeystruct.e.len <= 0) { ret = 1; goto ret; } - if (privatekeystruct.d_param.len <= 0) + if (privatekeystruct.d.len <= 0) { ret = 1; goto ret; } - if (privatekeystruct.p_param.len <= 0) + if (privatekeystruct.p.len <= 0) { ret = 1; goto ret; } - if (privatekeystruct.q_param.len <= 0) + if (privatekeystruct.q.len <= 0) { ret = 1; goto ret; diff --git a/packages/atchops/tests/test_rsapublicpopulate.c b/packages/atchops/tests/test_rsapublicpopulate.c index f30a0717..190ed8fa 100644 --- a/packages/atchops/tests/test_rsapublicpopulate.c +++ b/packages/atchops/tests/test_rsapublicpopulate.c @@ -19,13 +19,13 @@ int main() goto ret; } - if (publickeystruct.n_param.len <= 0) + if (publickeystruct.n.len <= 0) { ret = 1; goto ret; } - if (publickeystruct.e_param.len <= 0) + if (publickeystruct.e.len <= 0) { ret = 1; goto ret; diff --git a/packages/atchops/tests/test_rsasign.c b/packages/atchops/tests/test_rsasign.c index 9b0fd3dd..55a5e854 100644 --- a/packages/atchops/tests/test_rsasign.c +++ b/packages/atchops/tests/test_rsasign.c @@ -10,6 +10,8 @@ #define MESSAGE "_4a160d33-0c63-4800-bee0-ee254752f8c8@jeremy_0:6c987cc1-0dde-4ba1-af56-a9677086182" +#define EXPECTED_SIGNATURE "qaEysA8nF/aGjXiIqtHZzZbM90+fn2Ugpy5hgBf0izPoBR2orbeWVUJ1sI5fNMkMwOziRjA6j+AKcG4O/NaLYd31WOq4QzxDqRV+AY4d04mBZsa9wDd/30hSeUwCmubFNrHNSXi84HJS3D886FdByp2wRee5DIu6CalF4yCCpXO5YeTNNFvbk8Spqz3GY4cvjWhVW1ISBfQ928dWTuOkTfqiv/2cj9VAb460EoPeeWYabX1J2SBqUjRWsQsjiOiGiRYcft0DRc5TBru/oVGFrxbB+VL+HGc0Boi1T23FPoyg5FazF0yK2BBW0PWUqQ0BDny/1tZg7p8Wtv8ERHhxew=" + int main() { @@ -23,18 +25,26 @@ int main() if (ret != 0) goto ret; - size_t *signaturelen = malloc(sizeof(size_t)); + size_t signatureolen = 0; unsigned char *signature = malloc(sizeof(unsigned char) * SIGNATURE_BUFFER_LEN); + memset(signature, 0, SIGNATURE_BUFFER_LEN); const unsigned char *message = MESSAGE; const size_t messagelen = strlen(message); - ret = atchops_rsa_sign(privatekeystruct, ATCHOPS_MD_SHA256, &signature, signaturelen, message, messagelen); + ret = atchops_rsa_sign(privatekeystruct, ATCHOPS_MD_SHA256, message, messagelen, signature, SIGNATURE_BUFFER_LEN, &signatureolen); + printf("atchops_rsa_sign: %d\n", ret); if(ret != 0) goto ret; - ret = strncmp(signature, "AwsKWNqRHiCtdNJ0U5GXZ1H5obptEWVR1+A1kPhot4cdLfmulvBVXRaBIrP+jd2TSP2J/KNAgv2BDLH7DXUibdTnzJaKm/QKAjpwpuShnV6Y9KSWTnomBw9x9OWDkVrBzSo5rOFpHHOTZJhp4ygStKEzZDa108g8uP5PpkfzntO2eIVEOdMHoL9/yAkuYJcz+VmCH+1AJtCdeKfhjfmlk0bP72fwsait6pA3TW0iEll9ptZmlLjNtCTi982h1yNprh+XtrjMz7ClbJChQf3LLHiJMZ+7r4yKTrehdBVfxQoNNw9r2D7TBRaY8bXYwMombMHRuu0oVbqNU1jEs60NGQ==", *signaturelen); - if(ret != 0) goto ret; + ret = strncmp(signature, EXPECTED_SIGNATURE, signatureolen); + printf("strncmp: %d\n", ret); + if(ret != 0) + { + printf("signature len: %lu\n", signatureolen); + printf("signature: %s\n", signature); + goto ret; + } // printf("signature len: %lu\n", *signaturelen); // printf("signature: %s\n", signature); diff --git a/packages/atchops/tests/test_sha.c b/packages/atchops/tests/test_sha.c index 43e13977..125f5af5 100644 --- a/packages/atchops/tests/test_sha.c +++ b/packages/atchops/tests/test_sha.c @@ -7,13 +7,13 @@ int main() { int ret = 1; - const char *src = "Hello!"; + const unsigned char *src = "Hello!"; unsigned long dstlen = 32; unsigned char *dst = calloc(dstlen, sizeof(unsigned char)); unsigned long dstolen; - ret = atchops_sha_hash(dst, dstlen, &dstolen, (const unsigned char *) src, strlen(src), ATCHOPS_MD_SHA256); + ret = atchops_sha_hash(ATCHOPS_MD_SHA256, src, strlen(src), dst, dstlen, &dstolen); if(ret != 0) { printf("failed | atchops_sha_hash: %d\n", ret); From 68e424be8661c44c472b40ae3602925161e03440 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 00:28:54 -0400 Subject: [PATCH 05/26] feat: fix expected signature --- packages/atchops/src/rsa.c | 1 + packages/atchops/tests/test_rsasign.c | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/atchops/src/rsa.c b/packages/atchops/src/rsa.c index 276739a7..75f07c56 100644 --- a/packages/atchops/src/rsa.c +++ b/packages/atchops/src/rsa.c @@ -352,6 +352,7 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md --writtenlen; // printf("Challenge: %s\n", dst); + memset(signature, 0, signaturelen); memcpy(signature, dst, writtenlen); *signatureolen = writtenlen; diff --git a/packages/atchops/tests/test_rsasign.c b/packages/atchops/tests/test_rsasign.c index 55a5e854..7cea63d7 100644 --- a/packages/atchops/tests/test_rsasign.c +++ b/packages/atchops/tests/test_rsasign.c @@ -10,27 +10,27 @@ #define MESSAGE "_4a160d33-0c63-4800-bee0-ee254752f8c8@jeremy_0:6c987cc1-0dde-4ba1-af56-a9677086182" -#define EXPECTED_SIGNATURE "qaEysA8nF/aGjXiIqtHZzZbM90+fn2Ugpy5hgBf0izPoBR2orbeWVUJ1sI5fNMkMwOziRjA6j+AKcG4O/NaLYd31WOq4QzxDqRV+AY4d04mBZsa9wDd/30hSeUwCmubFNrHNSXi84HJS3D886FdByp2wRee5DIu6CalF4yCCpXO5YeTNNFvbk8Spqz3GY4cvjWhVW1ISBfQ928dWTuOkTfqiv/2cj9VAb460EoPeeWYabX1J2SBqUjRWsQsjiOiGiRYcft0DRc5TBru/oVGFrxbB+VL+HGc0Boi1T23FPoyg5FazF0yK2BBW0PWUqQ0BDny/1tZg7p8Wtv8ERHhxew=" +// #define EXPECTED_SIGNATURE "qaEysA8nF/aGjXiIqtHZzZbM90+fn2Ugpy5hgBf0izPoBR2orbeWVUJ1sI5fNMkMwOziRjA6j+AKcG4O/NaLYd31WOq4QzxDqRV+AY4d04mBZsa9wDd/30hSeUwCmubFNrHNSXi84HJS3D886FdByp2wRee5DIu6CalF4yCCpXO5YeTNNFvbk8Spqz3GY4cvjWhVW1ISBfQ928dWTuOkTfqiv/2cj9VAb460EoPeeWYabX1J2SBqUjRWsQsjiOiGiRYcft0DRc5TBru/oVGFrxbB+VL+HGc0Boi1T23FPoyg5FazF0yK2BBW0PWUqQ0BDny/1tZg7p8Wtv8ERHhxew=" +#define EXPECTED_SIGNATURE "AwsKWNqRHiCtdNJ0U5GXZ1H5obptEWVR1+A1kPhot4cdLfmulvBVXRaBIrP+jd2TSP2J/KNAgv2BDLH7DXUibdTnzJaKm/QKAjpwpuShnV6Y9KSWTnomBw9x9OWDkVrBzSo5rOFpHHOTZJhp4ygStKEzZDa108g8uP5PpkfzntO2eIVEOdMHoL9/yAkuYJcz+VmCH+1AJtCdeKfhjfmlk0bP72fwsait6pA3TW0iEll9ptZmlLjNtCTi982h1yNprh+XtrjMz7ClbJChQf3LLHiJMZ+7r4yKTrehdBVfxQoNNw9r2D7TBRaY8bXYwMombMHRuu0oVbqNU1jEs60NGQ=" int main() { + int ret = 1; - int ret = 0; - - size_t privatekeybase64len = strlen(PRIVATE_KEY_BASE64); const unsigned char *privatekeybase64 = PRIVATE_KEY_BASE64; + unsigned long privatekeybase64len = strlen(privatekeybase64); atchops_rsa_privatekey privatekeystruct; ret = atchops_rsa_populate_privatekey(privatekeybase64, privatekeybase64len, &privatekeystruct); if (ret != 0) goto ret; - size_t signatureolen = 0; unsigned char *signature = malloc(sizeof(unsigned char) * SIGNATURE_BUFFER_LEN); memset(signature, 0, SIGNATURE_BUFFER_LEN); + unsigned long signatureolen = 0; const unsigned char *message = MESSAGE; - const size_t messagelen = strlen(message); + const unsigned long messagelen = strlen(message); ret = atchops_rsa_sign(privatekeystruct, ATCHOPS_MD_SHA256, message, messagelen, signature, SIGNATURE_BUFFER_LEN, &signatureolen); printf("atchops_rsa_sign: %d\n", ret); @@ -43,6 +43,18 @@ int main() { printf("signature len: %lu\n", signatureolen); printf("signature: %s\n", signature); + printf("\n\n"); + for(int i = 0; i < signatureolen; i++) + printf("%02x ", signature[i]); + printf("\n"); + printf("\n\n"); + + printf("expected signature: %s\n", EXPECTED_SIGNATURE); + printf("\n\n"); + for(int i = 0; i < signatureolen; i++) + printf("%02x ", EXPECTED_SIGNATURE[i]); + printf("\n"); + printf("\n\n"); goto ret; } From 0a41cd93745739740ed4c6a7dd30176fb3113352 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 00:35:27 -0400 Subject: [PATCH 06/26] docs: root README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3c60cc1..6c13db72 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ cd at_c/packages/atclient cmake -S . -B build ``` -If you have installed MbedTLS and/or AtChops from source already, you can avoid fetching it everytime with `ATCLIENT_FETCH_MBEDTLS=OFF` and `ATCLIENT_FETCH_ATCHOPS=OFF` respectively: +Alternatively, if you have installed MbedTLS and/or AtChops from source already, you can avoid fetching it everytime with `ATCLIENT_FETCH_MBEDTLS=OFF` and `ATCLIENT_FETCH_ATCHOPS=OFF` respectively. Doing this drastically reduces the time it takes to configure the project: ```sh cmake -S . -B build -DATCLIENT_FETCH_MBEDTLS=OFF -DATCLIENT_FETCH_ATCHOPS=OFF From ea15d50cb6396184c861f9b22634f1c936ef1dff Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 00:36:01 -0400 Subject: [PATCH 07/26] chore: delete my temp main.c --- packages/atchops/CMakeLists.txt | 12 ------------ packages/atchops/src/main.c | 16 ---------------- 2 files changed, 28 deletions(-) delete mode 100644 packages/atchops/src/main.c diff --git a/packages/atchops/CMakeLists.txt b/packages/atchops/CMakeLists.txt index 35aa00d2..5e04edbb 100644 --- a/packages/atchops/CMakeLists.txt +++ b/packages/atchops/CMakeLists.txt @@ -110,15 +110,3 @@ if(ATCHOPS_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() - - -# Temp (DO NOT COMMIT) -add_executable(main src/main.c) -target_link_libraries(main PRIVATE atchops) -target_link_libraries(main PRIVATE MbedTLS::mbedcrypto) -target_include_directories(main PRIVATE ${atchops_include_dir}) - -install( - TARGETS main - ARCHIVE DESTINATION ${CMAKE_INSTALL_BINDIR} -) \ No newline at end of file diff --git a/packages/atchops/src/main.c b/packages/atchops/src/main.c deleted file mode 100644 index ecf2e7bd..00000000 --- a/packages/atchops/src/main.c +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include -#include -#include -#include "atchops/sha.h" -#include - -int main(int argc, char **argv) -{ - atchops_md_type md_type = ATCHOPS_MD_SHA256; - unsigned char a = mbedtls_md_get_size(mbedtls_md_info_from_type(md_type)); - - printf("a: %d\n", a); - return 0; -} \ No newline at end of file From f8c5442f98105553c899e460b7eed7fc2a777fff Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 01:05:13 -0400 Subject: [PATCH 08/26] fix: atchops::atchops alias --- packages/atchops/CMakeLists.txt | 10 +++++++++- packages/atclient/CMakeLists.txt | 6 +++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/atchops/CMakeLists.txt b/packages/atchops/CMakeLists.txt index 5e04edbb..507ca62f 100644 --- a/packages/atchops/CMakeLists.txt +++ b/packages/atchops/CMakeLists.txt @@ -81,7 +81,6 @@ install( ) # Installs atchops and mbedcrypto and tells `atchops-config.cmake` where to find stuff - set(TARGETS_TO_INSTALL ${PROJECT_NAME}) # install atchops if(ATCHOPS_FETCH_MBEDTLS) @@ -103,6 +102,15 @@ install( FILE ${PROJECT_NAME}-config.cmake ) +# Export in case people add us via add_subdirectory +export(PACKAGE ${PROJECT_NAME}) + +# Export in case people add us via add_subdirectory +export( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE "cmake/${PROJECT_NAME}-config.cmake") + # ######################################################### # Generate tests # ######################################################### diff --git a/packages/atclient/CMakeLists.txt b/packages/atclient/CMakeLists.txt index c625b4ae..be187eed 100644 --- a/packages/atclient/CMakeLists.txt +++ b/packages/atclient/CMakeLists.txt @@ -60,7 +60,6 @@ if(ATCLIENT_FETCH_MBEDTLS) URL https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.4.1.tar.gz URL_HASH SHA256=a420fcf7103e54e775c383e3751729b8fb2dcd087f6165befd13f28315f754f5 # hash for v3.4.1 .tar.gz release source code ) - FetchContent_MakeAvailable(MbedTLS) # ensures named dependencies have been populated message(STATUS "Successfully fetched MbedTLS") else() @@ -79,6 +78,8 @@ if(ATCLIENT_FETCH_ATCHOPS) SOURCE_DIR ${atchops_src_dir} ) FetchContent_MakeAvailable(atchops) # ensures named dependencies have been populated + add_library(atchops::atchops ALIAS atchops) # create alias for atchops so that we can use atchops::atchops + # we have to create an alias instead of namespace becuase I think FetchCOntent with SOURCE_DIR isn't actually installing it and is adding it as a subdirectory or similar. Unlike previous FetchContent, we provide a URL and I think that installs it message(STATUS "Successfully fetched `atchops` package") else() find_package(atchops REQUIRED CONFIG) @@ -93,8 +94,7 @@ endif() add_library(${PROJECT_NAME} STATIC ${atclient_srcs}) target_link_libraries(${PROJECT_NAME} - PUBLIC atchops - PUBLIC MbedTLS::mbedtls MbedTLS::mbedx509 + PUBLIC MbedTLS::mbedtls MbedTLS::mbedx509 atchops::atchops ) target_include_directories(${PROJECT_NAME} PUBLIC From 23e7f0f2249ee6854aa3c2dc6bb33360d39d7269 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 12:03:24 -0400 Subject: [PATCH 09/26] feat: subdirectory support --- packages/atchops/CMakeLists.txt | 9 ++++++--- packages/atclient/CMakeLists.txt | 32 +++++++++++++++++++++++++++++--- packages/repl/CMakeLists.txt | 7 ++++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/packages/atchops/CMakeLists.txt b/packages/atchops/CMakeLists.txt index 507ca62f..b1f256a2 100644 --- a/packages/atchops/CMakeLists.txt +++ b/packages/atchops/CMakeLists.txt @@ -102,17 +102,20 @@ install( FILE ${PROJECT_NAME}-config.cmake ) -# Export in case people add us via add_subdirectory +# ######################################################### +# 5. Export in case people add us via add_subdirectory +# ######################################################### export(PACKAGE ${PROJECT_NAME}) -# Export in case people add us via add_subdirectory export( EXPORT ${PROJECT_NAME}-config NAMESPACE ${PROJECT_NAME}:: FILE "cmake/${PROJECT_NAME}-config.cmake") +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + # ######################################################### -# Generate tests +# 6. Generate tests # ######################################################### if(ATCHOPS_BUILD_TESTS) enable_testing() diff --git a/packages/atclient/CMakeLists.txt b/packages/atclient/CMakeLists.txt index be187eed..cbaa21f4 100644 --- a/packages/atclient/CMakeLists.txt +++ b/packages/atclient/CMakeLists.txt @@ -78,8 +78,6 @@ if(ATCLIENT_FETCH_ATCHOPS) SOURCE_DIR ${atchops_src_dir} ) FetchContent_MakeAvailable(atchops) # ensures named dependencies have been populated - add_library(atchops::atchops ALIAS atchops) # create alias for atchops so that we can use atchops::atchops - # we have to create an alias instead of namespace becuase I think FetchCOntent with SOURCE_DIR isn't actually installing it and is adding it as a subdirectory or similar. Unlike previous FetchContent, we provide a URL and I think that installs it message(STATUS "Successfully fetched `atchops` package") else() find_package(atchops REQUIRED CONFIG) @@ -133,6 +131,12 @@ install( FILE ${PROJECT_NAME}-config.cmake ) +install( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE ${PROJECT_NAME}-targets.cmake # New: Required when writing our own -config.cmake file + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + # put everything inside of include/atclient into /usr/local/include/atclient install( DIRECTORY ${atclient_include_dir}/${PROJECT_NAME} @@ -140,8 +144,30 @@ install( ) # ######################################################### -# 5. Add tests +# 5. Export in case people use us in a subdirectory +# ######################################################### +export( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE ${PROJECT_NAME}-config.cmake +) + +export( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE ${PROJECT_NAME}-targets.cmake +) + +export( + PACKAGE ${PROJECT_NAME} +) + +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +# ######################################################### +# 6. Add tests # Run the tests via cd build && make all && cd tests && ctest +# configure with -DATCLIENT_BUILD_TESTS=ON # ######################################################### if(ATCLIENT_BUILD_TESTS) enable_testing() diff --git a/packages/repl/CMakeLists.txt b/packages/repl/CMakeLists.txt index 14c22236..6ecac908 100644 --- a/packages/repl/CMakeLists.txt +++ b/packages/repl/CMakeLists.txt @@ -8,8 +8,13 @@ project( ) include(GNUInstallDirs) +include(FetchContent) -find_package(atclient REQUIRED CONFIG) +FetchContent_Declare( + atclient + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../atclient +) +FetchContent_MakeAvailable(atclient) message(STATUS "atclient was found") From 9a2793542fa3582f2b459cb0d0e05bddc086013d Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 12:03:38 -0400 Subject: [PATCH 10/26] feat: fixing signatures --- packages/atchops/src/rsa.c | 3 +-- packages/atchops/tests/test_rsasign.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/atchops/src/rsa.c b/packages/atchops/src/rsa.c index 75f07c56..bd95935d 100644 --- a/packages/atchops/src/rsa.c +++ b/packages/atchops/src/rsa.c @@ -319,7 +319,7 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md // printf("hashlen: %lu\n", hashlen); // printf("hash: "); // printx(hash, hashlen); - int buflen = 256+1; // +1 for null terminator + int buflen = 256; // +1 for null terminator unsigned char *buf = malloc(sizeof(unsigned char) * buflen); memset(buf, 0, buflen); ret = mbedtls_rsa_pkcs1_sign(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg_ctx, MBEDTLS_MD_SHA256, hashlen, hash, buf); @@ -349,7 +349,6 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md { *(dst + writtenlen) = '\0'; } - --writtenlen; // printf("Challenge: %s\n", dst); memset(signature, 0, signaturelen); diff --git a/packages/atchops/tests/test_rsasign.c b/packages/atchops/tests/test_rsasign.c index 7cea63d7..98a27016 100644 --- a/packages/atchops/tests/test_rsasign.c +++ b/packages/atchops/tests/test_rsasign.c @@ -11,7 +11,7 @@ #define MESSAGE "_4a160d33-0c63-4800-bee0-ee254752f8c8@jeremy_0:6c987cc1-0dde-4ba1-af56-a9677086182" // #define EXPECTED_SIGNATURE "qaEysA8nF/aGjXiIqtHZzZbM90+fn2Ugpy5hgBf0izPoBR2orbeWVUJ1sI5fNMkMwOziRjA6j+AKcG4O/NaLYd31WOq4QzxDqRV+AY4d04mBZsa9wDd/30hSeUwCmubFNrHNSXi84HJS3D886FdByp2wRee5DIu6CalF4yCCpXO5YeTNNFvbk8Spqz3GY4cvjWhVW1ISBfQ928dWTuOkTfqiv/2cj9VAb460EoPeeWYabX1J2SBqUjRWsQsjiOiGiRYcft0DRc5TBru/oVGFrxbB+VL+HGc0Boi1T23FPoyg5FazF0yK2BBW0PWUqQ0BDny/1tZg7p8Wtv8ERHhxew=" -#define EXPECTED_SIGNATURE "AwsKWNqRHiCtdNJ0U5GXZ1H5obptEWVR1+A1kPhot4cdLfmulvBVXRaBIrP+jd2TSP2J/KNAgv2BDLH7DXUibdTnzJaKm/QKAjpwpuShnV6Y9KSWTnomBw9x9OWDkVrBzSo5rOFpHHOTZJhp4ygStKEzZDa108g8uP5PpkfzntO2eIVEOdMHoL9/yAkuYJcz+VmCH+1AJtCdeKfhjfmlk0bP72fwsait6pA3TW0iEll9ptZmlLjNtCTi982h1yNprh+XtrjMz7ClbJChQf3LLHiJMZ+7r4yKTrehdBVfxQoNNw9r2D7TBRaY8bXYwMombMHRuu0oVbqNU1jEs60NGQ=" +#define EXPECTED_SIGNATURE "AwsKWNqRHiCtdNJ0U5GXZ1H5obptEWVR1+A1kPhot4cdLfmulvBVXRaBIrP+jd2TSP2J/KNAgv2BDLH7DXUibdTnzJaKm/QKAjpwpuShnV6Y9KSWTnomBw9x9OWDkVrBzSo5rOFpHHOTZJhp4ygStKEzZDa108g8uP5PpkfzntO2eIVEOdMHoL9/yAkuYJcz+VmCH+1AJtCdeKfhjfmlk0bP72fwsait6pA3TW0iEll9ptZmlLjNtCTi982h1yNprh+XtrjMz7ClbJChQf3LLHiJMZ+7r4yKTrehdBVfxQoNNw9r2D7TBRaY8bXYwMombMHRuu0oVbqNU1jEs60NGQ==" int main() { From c0d01ac7e425cad9487deeb4b465a3d8a6bba353 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 16 Aug 2023 12:11:57 -0400 Subject: [PATCH 11/26] feat: repl --- packages/repl/src/main.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/repl/src/main.c b/packages/repl/src/main.c index 3c768303..68217e78 100644 --- a/packages/repl/src/main.c +++ b/packages/repl/src/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,8 +7,8 @@ #include #define ATSIGN "@jeremy_0" -// #define ATKEYS_FILE_PATH "/Users/jeremytubongbanua/.atsign/keys/@jeremy_0_key.atKeys" -#define ATKEYS_FILE_PATH "./@jeremy_0_key.atKeys" +#define ATKEYS_FILE_PATH "/Users/jeremytubongbanua/.atsign/keys/@jeremy_0_key.atKeys" +// #define ATKEYS_FILE_PATH "./@jeremy_0_key.atKeys" static void *without_at_symbol(char *atsign, char *buf) { @@ -157,17 +158,18 @@ int main() ret = atchops_rsa_populate_privatekey(pkamprivatekey, pkamprivatekeyolen, &pkamprivatekeystruct); - printf("n: %lu\n", pkamprivatekeystruct.n_param.len); - printf("e: %lu\n", pkamprivatekeystruct.e_param.len); + printf("n: %lu\n", pkamprivatekeystruct.n.len); + printf("e: %lu\n", pkamprivatekeystruct.e.len); // sign from response const size_t signaturelen = 32768; unsigned char *signature = malloc(sizeof(unsigned char) * signaturelen); + unsigned long signatureolen = 0; memset(signature, 0, signaturelen); - atchops_rsa_sign(pkamprivatekeystruct, ATCHOPS_MD_SHA256, &signature, &signaturelen, from_response, strlen(from_response)); + atchops_rsa_sign(pkamprivatekeystruct, ATCHOPS_MD_SHA256, from_response, strlen(from_response), signature, signaturelen, &signatureolen); - printf("signature: \"%s\"\n", signature); + printf("signature: \"%.*s\"\n", (int) signatureolen, signature); // send pkam command @@ -176,13 +178,14 @@ int main() memset(pkamcommand, 0, pkamcommandlen); strcat(pkamcommand, "pkam:"); - strncat(pkamcommand, signature, signaturelen); + strcat(pkamcommand, signature); strcat(pkamcommand, "\r\n"); printf("pkam command: \"%s\"\n", pkamcommand); - atclient_connection_send(&secondary_connection, recv, recvlen, &olen, pkamcommand, strlen(pkamcommand)); + ret = atclient_connection_send(&secondary_connection, recv, recvlen, &olen, pkamcommand, strlen(pkamcommand)); + printf("signature olen: %lu\n", signatureolen); printf("\"%.*s\"\n", (int) olen, recv); size_t commandlen = 32768; From 373ed2af58617384c07f309635f7d36e579243da Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 17 Aug 2023 23:54:09 -0400 Subject: [PATCH 12/26] feat: atclient_esp32 --- packages/atchops/CMakeLists.txt | 197 ++++---- packages/atchops/install-here.sh | 4 +- packages/atchops/install-system.sh | 2 +- packages/atchops/run-tests.sh | 6 +- packages/atchops/src/aes_ctr.c | 6 +- packages/atchops/src/rsa.c | 54 +-- packages/atclient/CMakeLists.txt | 446 +++++++----------- packages/atclient/install-system.sh | 4 +- packages/atclient/run-tests.sh | 2 +- packages/atclient/src/CMakeLists.txt | 20 - packages/atclient/src/connection.c | 4 +- .../targets/esp32_espidf/CMakeLists.txt | 6 - packages/atclient_esp32/CMakeLists.txt | 8 + .../atclient_esp32/include/atchops/aes_ctr.h | 31 ++ .../atclient_esp32/include/atchops/base64.h | 25 + packages/atclient_esp32/include/atchops/rsa.h | 84 ++++ packages/atclient_esp32/include/atchops/sha.h | 14 + .../include/atclient/at_logger.h | 9 + .../include/atclient/atkeys_filereader.h | 59 +++ .../include/atclient/connection.h | 59 +++ packages/atclient_esp32/lib/libatchops.a | Bin 0 -> 74662 bytes packages/atclient_esp32/lib/libatclient.a | Bin 0 -> 81616 bytes packages/atclient_esp32/main/CMakeLists.txt | 1 + .../main}/main.c | 3 +- packages/repl/CMakeLists.txt | 11 +- packages/repl/run.sh | 5 - 26 files changed, 614 insertions(+), 446 deletions(-) delete mode 100644 packages/atclient/src/CMakeLists.txt delete mode 100644 packages/atclient/targets/esp32_espidf/CMakeLists.txt create mode 100644 packages/atclient_esp32/CMakeLists.txt create mode 100644 packages/atclient_esp32/include/atchops/aes_ctr.h create mode 100644 packages/atclient_esp32/include/atchops/base64.h create mode 100644 packages/atclient_esp32/include/atchops/rsa.h create mode 100644 packages/atclient_esp32/include/atchops/sha.h create mode 100644 packages/atclient_esp32/include/atclient/at_logger.h create mode 100644 packages/atclient_esp32/include/atclient/atkeys_filereader.h create mode 100644 packages/atclient_esp32/include/atclient/connection.h create mode 100644 packages/atclient_esp32/lib/libatchops.a create mode 100644 packages/atclient_esp32/lib/libatclient.a create mode 100644 packages/atclient_esp32/main/CMakeLists.txt rename packages/{atclient/targets/esp32_espidf => atclient_esp32/main}/main.c (78%) diff --git a/packages/atchops/CMakeLists.txt b/packages/atchops/CMakeLists.txt index b1f256a2..852c9058 100644 --- a/packages/atchops/CMakeLists.txt +++ b/packages/atchops/CMakeLists.txt @@ -1,13 +1,5 @@ cmake_minimum_required(VERSION 3.19) -project( - atchops - VERSION 1.0.0 - DESCRIPTION "The at_client implemented in C" - HOMEPAGE_URL https://atsign.com - LANGUAGES C -) - # ######################################################### # 0. Variables - you are free to edit anything in this step # ######################################################### @@ -27,97 +19,124 @@ set(atchops_include_dir option(ATCHOPS_FETCH_MBEDTLS "Fetch MbedTLS from GitHub" ON) # If OFF, we assume MbedTLS is installed on the system and was already built from source option(ATCHOPS_BUILD_TESTS "Build tests" OFF) -# ######################################################### -# 1. Include CMake modules -# ######################################################### -include(FetchContent) - -# ${CMAKE_INSTALL_*} variables are defined in GNUInstallDirs and changes according to OS. E.g. on Linux & MacOS, ${CMAKE_INSTALL_LIBDIR} is /usr/local/lib, but on Windows it may be C:\Program Files\atchops\lib -include(GNUInstallDirs) - -# ######################################################### -# 2. find_package MbedTLS -# #########################################################ß -if(ATCHOPS_FETCH_MBEDTLS) - FetchContent_Declare( - MbedTLS - URL https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.4.1.tar.gz - URL_HASH SHA256=a420fcf7103e54e775c383e3751729b8fb2dcd087f6165befd13f28315f754f5 # hash for v3.4.1 .tar.gz release source code +if(ESP_PLATFORM) + idf_component_register( + SRCS ${atchops_srcs} + INCLUDE_DIRS ${atchops_include_dir} + REQUIRES mbedtls + ) + add_custom_command( + TARGET ${COMPONENT_LIB} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${atchops_include_dir} ${CMAKE_SOURCE_DIR}/include + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/lib/lib${COMPONENT_NAME}.a + COMMENT "Copying built archive file and header to lib directory..." ) - FetchContent_MakeAvailable(MbedTLS) # ensures named dependencies have been populated - message(STATUS "Successfully fetched MbedTLS") -else() - find_package(MbedTLS REQUIRED CONFIG) # this will look for MbedTLSConfig.cmake in /usr/local/lib/cmake/MbedTLS/MbedTLSConfig.cmake - message(STATUS "Successfully found package MbedTLS") endif() -# MbedTLS::mbedtls, MbedTLS::mbedcrypto, and MbedTLS::mbedx509 are now available - -# ######################################################### -# 3. Create atchops target -# ######################################################### -add_library(atchops STATIC ${atchops_srcs}) - -# add include/atchops/*.h to `atchops` -# we put *.h inside of nested atchops/ directory so that it will be included like #include "atchops/atchops.h" -target_include_directories(atchops PUBLIC - $ # when `cmake ..` look for headers when building in ./packages/atchops/include/atchops/*.h - $ # when `make install` look inside of something like /usr/local/include/** +project( + atchops + VERSION 1.0.0 + DESCRIPTION "The at_client implemented in C" + HOMEPAGE_URL https://atsign.com + LANGUAGES C ) -# add MbedTLS dependencies to `atchops` -target_link_libraries(atchops PUBLIC MbedTLS::mbedcrypto) - -# ######################################################### -# 4. Install atchops -# doing `make install` or `cmake --build build --target install` (<- recommended) will run all this stuff below -# (this stuff is pretty much analogous to doing something like dart pub add) -# ######################################################### - -# Installs headers to /usr/local/include/atchops/*.h -install( - DIRECTORY ${atchops_include_dir}/${PROJECT_NAME} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # install goes to: /usr/local/include/atchops/*.h -) +if(NOT ESP_PLATFORM) + # ######################################################### + # 1. Include CMake modules + # ######################################################### + + # FetchContent is a module that allows us to fetch dependencies from GitHub, URL, etc. and build them from source in configure step + include(FetchContent) + + # ${CMAKE_INSTALL_*} variables are defined in GNUInstallDirs and changes according to OS. E.g. on Linux & MacOS, ${CMAKE_INSTALL_LIBDIR} is /usr/local/lib, but on Windows it may be C:\Program Files\atchops\lib + include(GNUInstallDirs) + + # ######################################################### + # 2. find_package MbedTLS + # #########################################################ß + if(ATCHOPS_FETCH_MBEDTLS) + FetchContent_Declare( + MbedTLS + URL https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.4.1.tar.gz + URL_HASH SHA256=a420fcf7103e54e775c383e3751729b8fb2dcd087f6165befd13f28315f754f5 # hash for v3.4.1 .tar.gz release source code + ) + FetchContent_MakeAvailable(MbedTLS) # ensures named dependencies have been populated + message(STATUS "Successfully fetched MbedTLS") + else() + find_package(MbedTLS REQUIRED CONFIG) # this will look for MbedTLSConfig.cmake in /usr/local/lib/cmake/MbedTLS/MbedTLSConfig.cmake + message(STATUS "Successfully found package MbedTLS") + endif() + + # MbedTLS::mbedtls, MbedTLS::mbedcrypto, and MbedTLS::mbedx509 are now available + + # ######################################################### + # 3. Create atchops target + # ######################################################### + add_library(atchops STATIC ${atchops_srcs}) + + # add include/atchops/*.h to `atchops` + # we put *.h inside of nested atchops/ directory so that it will be included like #include "atchops/atchops.h" + target_include_directories(atchops PUBLIC + $ # when `cmake ..` look for headers when building in ./packages/atchops/include/atchops/*.h + $ # when `make install` look inside of something like /usr/local/include/** + ) -# Installs atchops and mbedcrypto and tells `atchops-config.cmake` where to find stuff -set(TARGETS_TO_INSTALL ${PROJECT_NAME}) # install atchops + # add MbedTLS dependencies to `atchops` + target_link_libraries(atchops PUBLIC MbedTLS::mbedcrypto) -if(ATCHOPS_FETCH_MBEDTLS) - # if we fetched MbedTLS, it is not installed, so we need to install it - list(APPEND TARGETS_TO_INSTALL mbedcrypto) -endif() + # ######################################################### + # 4. Install atchops + # doing `make install` or `cmake --build build --target install` (<- recommended) will run all this stuff below + # (this stuff is pretty much analogous to doing something like dart pub add) + # ######################################################### -install( - TARGETS ${TARGETS_TO_INSTALL} - EXPORT ${PROJECT_NAME}-config - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # install goes to: /usr/local/lib/atchops/libatchops.a -) + # Installs headers to /usr/local/include/atchops/*.h + install( + DIRECTORY ${atchops_include_dir}/${PROJECT_NAME} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # install goes to: /usr/local/include/atchops/*.h + ) -# Export atchops-config.cmake to /usr/local/lib/cmake/atchops/atchops-config.cmake -install( - EXPORT ${PROJECT_NAME}-config - NAMESPACE ${PROJECT_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} - FILE ${PROJECT_NAME}-config.cmake -) + # Installs atchops and mbedcrypto and tells `atchops-config.cmake` where to find stuff + set(TARGETS_TO_INSTALL ${PROJECT_NAME}) # install atchops -# ######################################################### -# 5. Export in case people add us via add_subdirectory -# ######################################################### -export(PACKAGE ${PROJECT_NAME}) + if(ATCHOPS_FETCH_MBEDTLS) + # if we fetched MbedTLS, it is not installed, so we need to install it + list(APPEND TARGETS_TO_INSTALL mbedcrypto) + endif() -export( - EXPORT ${PROJECT_NAME}-config - NAMESPACE ${PROJECT_NAME}:: - FILE "cmake/${PROJECT_NAME}-config.cmake") + install( + TARGETS ${TARGETS_TO_INSTALL} + EXPORT ${PROJECT_NAME}-config + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # install goes to: /usr/local/lib/atchops/libatchops.a + ) -add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + # Export atchops-config.cmake to /usr/local/lib/cmake/atchops/atchops-config.cmake + install( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + FILE ${PROJECT_NAME}-config.cmake + ) -# ######################################################### -# 6. Generate tests -# ######################################################### -if(ATCHOPS_BUILD_TESTS) - enable_testing() - add_subdirectory(tests) -endif() + # ######################################################### + # 5. Export in case people add us via add_subdirectory + # ######################################################### + export(PACKAGE ${PROJECT_NAME}) + + export( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE "cmake/${PROJECT_NAME}-config.cmake") + + add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + + # ######################################################### + # 6. Generate tests + # ######################################################### + if(ATCHOPS_BUILD_TESTS) + enable_testing() + add_subdirectory(tests) + endif() +endif() \ No newline at end of file diff --git a/packages/atchops/install-here.sh b/packages/atchops/install-here.sh index 1b1457ed..1c531386 100755 --- a/packages/atchops/install-here.sh +++ b/packages/atchops/install-here.sh @@ -3,5 +3,5 @@ set -eu sudo rm -f build/CMakeCache.txt sudo rm -rf install mkdir -p install -cmake -S . -B build -DCMAKE_INSTALL_PREFIX=./install -sudo cmake --build build --target install \ No newline at end of file +cmake -S . -B build -DCMAKE_INSTALL_PREFIX=./install -DATCHOPS_FETCH_MBEDTLS=ON +cmake --build build --target install/local \ No newline at end of file diff --git a/packages/atchops/install-system.sh b/packages/atchops/install-system.sh index 4a9ad8a0..e9b6af48 100755 --- a/packages/atchops/install-system.sh +++ b/packages/atchops/install-system.sh @@ -1,5 +1,5 @@ #!/bin/bash set -eu sudo rm -f build/CMakeCache.txt -cmake -S . -B build +cmake -S . -B build -DATCHOPS_FETCH_MBEDTLS=ON sudo cmake --build build --target install \ No newline at end of file diff --git a/packages/atchops/run-tests.sh b/packages/atchops/run-tests.sh index c0e516f6..8c0e366d 100755 --- a/packages/atchops/run-tests.sh +++ b/packages/atchops/run-tests.sh @@ -1,7 +1,9 @@ #!/bin/bash set -eu -cmake -S . -B build -DATCHOPS_BUILD_TESTS=ON +# sudo rm -rf build +sudo cmake -S . -B build -DATCHOPS_BUILD_TESTS=ON -DATCHOPS_FETCH_MBEDTLS=ON -DCMAKE_INSTALL_PREFIX=./install +sudo rm -rf install sudo cmake --build build --target all cd build/tests -ctest --output-on-failure +sudo ctest --output-on-failure -V cd ../.. \ No newline at end of file diff --git a/packages/atchops/src/aes_ctr.c b/packages/atchops/src/aes_ctr.c index ec4d39c2..45c51ff7 100644 --- a/packages/atchops/src/aes_ctr.c +++ b/packages/atchops/src/aes_ctr.c @@ -27,7 +27,7 @@ int atchops_aes_ctr_encrypt( unsigned char *key = malloc(sizeof(unsigned char) * keylen); unsigned long keyolen; - ret = atchops_base64_decode(keybase64, keybase64len, key, keylen, &keyolen); + ret = atchops_base64_decode((const unsigned char *) keybase64, keybase64len, key, keylen, &keyolen); printf("atchops_base64_decode: %d\n", ret); // printf("aes_key:\n"); // for(int i = 0; i < keyolen; i++) @@ -113,7 +113,7 @@ int atchops_aes_ctr_decrypt( const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, - const size_t plaintextlen, + const unsigned long plaintextlen, unsigned long *plaintextolen) { int ret = 1; @@ -124,7 +124,7 @@ int atchops_aes_ctr_decrypt( memset(key, 0, keylen); unsigned long keyolen = 0; - ret = atchops_base64_decode(keybase64, keybase64len, key, keylen, &keyolen); + ret = atchops_base64_decode((const unsigned char *) keybase64, keybase64len, key, keylen, &keyolen); // printf("atchops_base64_decode: %d\n", ret); // printf("aes_key: %lu\n", keyolen); // for(int i = 0; i < keyolen; i++) diff --git a/packages/atchops/src/rsa.c b/packages/atchops/src/rsa.c index bd95935d..a6f411c9 100644 --- a/packages/atchops/src/rsa.c +++ b/packages/atchops/src/rsa.c @@ -10,7 +10,7 @@ #include "atchops/base64.h" #include "atchops/sha.h" -static void printx(const unsigned char *buf, const size_t len) +static void printx(const unsigned char *buf, const unsigned long len) { for (int i = 0; i < len; i++) { @@ -19,15 +19,15 @@ static void printx(const unsigned char *buf, const size_t len) printf("\n"); } -int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t publickeybase64len, atchops_rsa_publickey *publickeystruct) +int atchops_rsa_populate_publickey(const char *publickeybase64, const unsigned long publickeybase64len, atchops_rsa_publickey *publickeystruct) { int ret = 0; // 1. base64 decode the key - size_t dstlen = 8192; + unsigned long dstlen = 8192; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); - size_t writtenlen = 0; - ret = atchops_base64_decode(publickeybase64, publickeybase64len, dst, dstlen, &writtenlen); + unsigned long writtenlen = 0; + ret = atchops_base64_decode((const unsigned char *) publickeybase64, publickeybase64len, dst, dstlen, &writtenlen); if (ret != 0) goto ret; @@ -37,7 +37,7 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub const unsigned char *end = dst + (writtenlen); - size_t *lengthread = malloc(sizeof(size_t)); + unsigned long *lengthread = malloc(sizeof(unsigned long)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); // printf("ret: %d\n", ret); if (ret != 0) @@ -47,7 +47,7 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub // printf("*(dst+1) now points to : %02x\n", *(dst+1)); // printf("*(dst+2) now points to : %02x\n", *(dst+2)); - size_t *lengthread2 = malloc(sizeof(size_t)); + unsigned long *lengthread2 = malloc(sizeof(unsigned long)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread2, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); // printf("ret: %d\n", ret); if (ret != 0) @@ -61,7 +61,7 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub // printf("*(dst+1) now points to : %02x\n", *(dst+1)); // printf("*(dst+2) now points to : %02x\n", *(dst+2)); - size_t *lengthread3 = malloc(sizeof(size_t)); + unsigned long *lengthread3 = malloc(sizeof(unsigned long)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread3, MBEDTLS_ASN1_BIT_STRING); // printf("ret: %d\n", ret); if (ret != 0) @@ -111,15 +111,15 @@ int atchops_rsa_populate_publickey(const char *publickeybase64, const size_t pub } } -int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t privatekeybase64len, atchops_rsa_privatekey *privatekeystruct) +int atchops_rsa_populate_privatekey(const char *privatekeybase64, const unsigned long privatekeybase64len, atchops_rsa_privatekey *privatekeystruct) { int ret = 1; - size_t dstlen = 8196; + unsigned long dstlen = 8196; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); memset(dst, 0, dstlen); - size_t writtenlen = 0; - ret = atchops_base64_decode(privatekeybase64, privatekeybase64len, dst, dstlen, &writtenlen); + unsigned long writtenlen = 0; + ret = atchops_base64_decode((const unsigned char *) privatekeybase64, privatekeybase64len, dst, dstlen, &writtenlen); if (ret != 0) goto ret; @@ -128,10 +128,10 @@ int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t p // printf("writtenlen: %lu\n", *writtenlen); // printf("\n"); - char *end = dst + writtenlen; + unsigned char *end = dst + writtenlen; // printf("1st get tag\n"); - size_t *lengthread = malloc(sizeof(size_t)); + unsigned long *lengthread = malloc(sizeof(unsigned long)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret != 0) goto ret; @@ -144,7 +144,7 @@ int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t p // printf("*(dst+4) now points to : %02x\n", *(dst+4)); // printf("2nd get tag\n"); - size_t *lengthread2 = malloc(sizeof(size_t)); + unsigned long *lengthread2 = malloc(sizeof(unsigned long)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread2, MBEDTLS_ASN1_INTEGER); if (ret != 0) goto ret; @@ -158,7 +158,7 @@ int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t p // printf("*(dst+4) now points to : %02x\n", *(dst+4)); // printf("3rd get tag\n"); - size_t *lengthread3 = malloc(sizeof(size_t)); + unsigned long *lengthread3 = malloc(sizeof(unsigned long)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread3, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret != 0) goto ret; @@ -172,7 +172,7 @@ int atchops_rsa_populate_privatekey(const char *privatekeybase64, const size_t p // printf("*(dst+4) now points to : %02x\n", *(dst+4)); // printf("4th get tag\n"); - size_t *lengthread4 = malloc(sizeof(size_t)); + unsigned long *lengthread4 = malloc(sizeof(unsigned long)); ret = mbedtls_asn1_get_tag(&dst, end, lengthread4, 0x04); if (ret != 0) goto ret; @@ -252,10 +252,10 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md { int ret = 1; // error, until successful. - const size_t hashlen = 32; + const unsigned long hashlen = 32; unsigned char *hash = malloc(sizeof(char) * hashlen); memset(hash, 0, hashlen); - size_t hasholen = 0; + unsigned long hasholen = 0; ret = atchops_sha_hash(mdtype, message, messagelen, hash, hashlen, &hasholen); if (ret != 0) goto ret; @@ -338,7 +338,7 @@ int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type md unsigned long dstlen = 4096; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); memset(dst, 0, dstlen); - size_t writtenlen = 0; + unsigned long writtenlen = 0; ret = atchops_base64_encode(buf, 256, dst, dstlen, &writtenlen); printf("atchops_base64_encode: %d\n", ret); if (ret != 0) @@ -394,9 +394,9 @@ int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned ch // printf("base64 encoding...\n"); // base64 encode the plain text - size_t dstlen = 2048; + unsigned long dstlen = 2048; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); - size_t olen = 0; + unsigned long olen = 0; ret = atchops_base64_encode(plaintext, plaintextlen, dst, dstlen, &olen); // printf("atchops_base64_encode_1: %d\n", ret); if (ret != 0) @@ -415,7 +415,7 @@ int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned ch // printf("encrypting...\n"); // encrypt the base64 encoded text - size_t outputlen = 256; // 256 bytes is the result of an RSA + unsigned long outputlen = 256; // 256 bytes is the result of an RSA unsigned char *output = malloc(sizeof(unsigned char) * outputlen); memset(output, 0, outputlen); ret = mbedtls_rsa_pkcs1_encrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg_ctx, plaintextlen, plaintext, output); @@ -486,10 +486,10 @@ int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned goto ret; // base64 decode the ciphertext - const size_t dstlen = 8192; + const unsigned long dstlen = 8192; unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); memset(dst, 0, dstlen); - size_t writtenlen = 0; + unsigned long writtenlen = 0; ret = atchops_base64_decode(ciphertextbase64, ciphertextbase64len, dst, dstlen, &writtenlen); // printf("atchops_base64_decode: %d\n", ret); if (ret != 0) @@ -505,10 +505,10 @@ int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned goto ret; // rsa decrypt dst - const size_t outputmaxlen = 4096; + const unsigned long outputmaxlen = 4096; unsigned char *output = malloc(sizeof(unsigned char) * outputmaxlen); memset(output, 0, outputmaxlen); - size_t writtenlen2 = 0; + unsigned long writtenlen2 = 0; ret = mbedtls_rsa_pkcs1_decrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg_ctx, &writtenlen2, dst, output, outputmaxlen); // printf("mbedtls_rsa_pkcs1_decrypt: %d\n", ret); if (ret != 0) diff --git a/packages/atclient/CMakeLists.txt b/packages/atclient/CMakeLists.txt index cbaa21f4..f1907624 100644 --- a/packages/atclient/CMakeLists.txt +++ b/packages/atclient/CMakeLists.txt @@ -1,15 +1,7 @@ cmake_minimum_required(VERSION 3.19) -project( - atclient - VERSION 0.0.1 - DESCRIPTION "Atsign technolgoy client library" - HOMEPAGE_URL https://atsign.com - LANGUAGES C -) - # ######################################################### -# 0a. Variables - you are free to edit anything in this step +# 0. Variables - you are free to edit anything in this step # ######################################################### # apparently globs are bad practice, manually add your src files here @@ -23,294 +15,188 @@ set(atclient_include_dir ${CMAKE_CURRENT_LIST_DIR}/include # we do not include nested folder here because we want client to include them like `#include "atclient/xyz.h" ) -option(ATCLIENT_BUILD_TESTS "Build atclient tests" OFF) - -# ######################################################### -# 0b. Build atchops locally or use already installed atchops -# ######################################################### - -# can use during the configuration command -# example: `cmake -S . -B build -DFETCH_ATCHOPS=OFF` to not rebuild atchops everytime -option(ATCLIENT_FETCH_ATCHOPS "Use Local ATCHOPS" ON) -option(ATCLIENT_FETCH_MBEDTLS "Fetch MbedTLS via releases" ON) - -if(ATCLIENT_FETCH_ATCHOPS) - set(atchops_src_dir ${CMAKE_CURRENT_LIST_DIR}/../atchops) - message(STATUS "Building Local ATCHOPS: ${USE_LOCAL_ATCHOPS} from DIR: ${atchops_src_dir}") -endif() - -# ######################################################### -# 1. Include stuff -# ######################################################### - -# FetchContent is a CMake v3.11+ module that downloads content at configure time -include(FetchContent) - -# Difference between FetchContent and ExternalProject: https://cmake.org/cmake/help/latest/module/FetchContent.html#id6 - -# ${CMAKE_INSTALL_*} variables are defined in GNUInstallDirs and changes according to OS. E.g. on Linux & MacOS, ${CMAKE_INSTALL_LIBDIR} is /usr/local/lib, but on Windows it may be C:\Program Files\atchops\lib -include(GNUInstallDirs) - -# ######################################################### -# 2A. Get MbedTLS::mbedtls -# ######################################################### -if(ATCLIENT_FETCH_MBEDTLS) - FetchContent_Declare( - MbedTLS - URL https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.4.1.tar.gz - URL_HASH SHA256=a420fcf7103e54e775c383e3751729b8fb2dcd087f6165befd13f28315f754f5 # hash for v3.4.1 .tar.gz release source code +if(ESP_PLATFORM) + idf_component_register( + SRCS ${atclient_srcs} + INCLUDE_DIRS ${atclient_include_dir} + REQUIRES mbedtls atchops ) - FetchContent_MakeAvailable(MbedTLS) # ensures named dependencies have been populated - message(STATUS "Successfully fetched MbedTLS") -else() - find_package(MbedTLS REQUIRED CONFIG) - message(STATUS "Successfully found `MbedTLS` package") -endif() - -# MbedTLS::mbedtls, MbedTLS::mbedcrypto, and MbedTLS::mbedx50 are now available via MbedTLS namespace - -# ######################################################### -# 2B. Get atchops -# ######################################################### -if(ATCLIENT_FETCH_ATCHOPS) - FetchContent_Declare( - atchops - SOURCE_DIR ${atchops_src_dir} + add_custom_command( + TARGET ${COMPONENT_LIB} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${atclient_include_dir} ${CMAKE_SOURCE_DIR}/include + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/lib/lib${COMPONENT_NAME}.a + COMMENT "Copying built archive file and header to lib directory..." ) - FetchContent_MakeAvailable(atchops) # ensures named dependencies have been populated - message(STATUS "Successfully fetched `atchops` package") -else() - find_package(atchops REQUIRED CONFIG) - message(STATUS "Successfully found `atchops` package") endif() -# atchops::atchops is now available - -# ######################################################### -# 3. Create atclient library -# ######################################################### -add_library(${PROJECT_NAME} STATIC ${atclient_srcs}) - -target_link_libraries(${PROJECT_NAME} - PUBLIC MbedTLS::mbedtls MbedTLS::mbedx509 atchops::atchops +project( + atclient + VERSION 0.0.1 + DESCRIPTION "Atsign technolgoy client library" + HOMEPAGE_URL https://atsign.com + LANGUAGES C ) -target_include_directories(${PROJECT_NAME} PUBLIC - $ - $ -) +if(NOT ESP_PLATFORM) # build for Desktop + option(ATCLIENT_BUILD_TESTS "Build atclient tests" OFF) + + option(ATCLIENT_FETCH_ATCHOPS "Use Local ATCHOPS" ON) + option(ATCLIENT_FETCH_MBEDTLS "Fetch MbedTLS via releases" ON) + + # ######################################################### + # 0b. Build atchops locally or use already installed atchops + # ######################################################### + + # can use during the configuration command + # example: `cmake -S . -B build -DFETCH_ATCHOPS=OFF` to not rebuild atchops everytime + if(ATCLIENT_FETCH_ATCHOPS) + set(atchops_src_dir ${CMAKE_CURRENT_LIST_DIR}/../atchops) + message(STATUS "Building Local ATCHOPS: ${USE_LOCAL_ATCHOPS} from DIR: ${atchops_src_dir}") + endif() + + # ######################################################### + # 1. Include stuff + # ######################################################### + + # FetchContent is a CMake v3.11+ module that downloads content at configure time + include(FetchContent) + + # Difference between FetchContent and ExternalProject: https://cmake.org/cmake/help/latest/module/FetchContent.html#id6 + + # ${CMAKE_INSTALL_*} variables are defined in GNUInstallDirs and changes according to OS. E.g. on Linux & MacOS, ${CMAKE_INSTALL_LIBDIR} is /usr/local/lib, but on Windows it may be C:\Program Files\atchops\lib + include(GNUInstallDirs) + + # ######################################################### + # 2A. Get MbedTLS::mbedtls + # ######################################################### + if(ATCLIENT_FETCH_MBEDTLS) + FetchContent_Declare( + MbedTLS + URL https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.4.1.tar.gz + URL_HASH SHA256=a420fcf7103e54e775c383e3751729b8fb2dcd087f6165befd13f28315f754f5 # hash for v3.4.1 .tar.gz release source code + ) + FetchContent_MakeAvailable(MbedTLS) # ensures named dependencies have been populated + message(STATUS "Successfully fetched MbedTLS") + else() + find_package(MbedTLS REQUIRED CONFIG) + message(STATUS "Successfully found `MbedTLS` package") + endif() + + # MbedTLS::mbedtls, MbedTLS::mbedcrypto, and MbedTLS::mbedx50 are now available via MbedTLS namespace + + # ######################################################### + # 2B. Get atchops + # ######################################################### + if(ATCLIENT_FETCH_ATCHOPS) + FetchContent_Declare( + atchops + SOURCE_DIR ${atchops_src_dir} + ) + FetchContent_MakeAvailable(atchops) # ensures named dependencies have been populated + message(STATUS "Successfully fetched `atchops` package") + else() + find_package(atchops REQUIRED CONFIG) + message(STATUS "Successfully found `atchops` package") + endif() + + # atchops::atchops is now available + + # ######################################################### + # 3. Create atclient library + # ######################################################### + add_library(${PROJECT_NAME} STATIC ${atclient_srcs}) + + target_link_libraries(${PROJECT_NAME} + PUBLIC MbedTLS::mbedtls MbedTLS::mbedx509 atchops::atchops + ) -# ######################################################### -# 4. Install atclient library -# This step configures running `cmake --build build --target install` (which is the same thing as `make install`) -# ######################################################### + target_include_directories(${PROJECT_NAME} PUBLIC + $ + $ + ) -# exports atclient-config.cmake to /usr/local/lib/cmake/atclient/atclient-config.cmake -set(TARGETS_TO_INSTALL ${PROJECT_NAME}) # install atclient + # ######################################################### + # 4. Install atclient library + # This step configures running `cmake --build build --target install` (which is the same thing as `make install`) + # ######################################################### -if(ATCLIENT_FETCH_ATCHOPS) # if we are fetching atchops, it was not installed, so we do not need to export it - list(APPEND TARGETS_TO_INSTALL atchops) # install atchops -endif() + # exports atclient-config.cmake to /usr/local/lib/cmake/atclient/atclient-config.cmake + set(TARGETS_TO_INSTALL ${PROJECT_NAME}) # install atclient -if(ATCLIENT_FETCH_MBEDTLS) # if we are fetching mbedtls, it was not installed, so we do not need to export it - list(APPEND TARGETS_TO_INSTALL mbedtls mbedx509 mbedcrypto) # install mbedtls -endif() + if(ATCLIENT_FETCH_ATCHOPS) # if we are fetching atchops, it was not installed, so we do not need to export it + list(APPEND TARGETS_TO_INSTALL atchops) # install atchops + endif() -# install atclient library to /usr/local/lib/libatclient.a -install( - TARGETS ${TARGETS_TO_INSTALL} - EXPORT ${PROJECT_NAME}-config - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) + if(ATCLIENT_FETCH_MBEDTLS) # if we are fetching mbedtls, it was not installed, so we do not need to export it + list(APPEND TARGETS_TO_INSTALL mbedtls mbedx509 mbedcrypto) # install mbedtls + endif() -# export the config to /usr/local/lib/cmake/atclient/atclient-config.cmake -install( - EXPORT ${PROJECT_NAME}-config - NAMESPACE ${PROJECT_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} - FILE ${PROJECT_NAME}-config.cmake -) + # install atclient library to /usr/local/lib/libatclient.a + install( + TARGETS ${TARGETS_TO_INSTALL} + EXPORT ${PROJECT_NAME}-config + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) -install( - EXPORT ${PROJECT_NAME}-config - NAMESPACE ${PROJECT_NAME}:: - FILE ${PROJECT_NAME}-targets.cmake # New: Required when writing our own -config.cmake file - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + # export the config to /usr/local/lib/cmake/atclient/atclient-config.cmake + install( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + FILE ${PROJECT_NAME}-config.cmake + ) -# put everything inside of include/atclient into /usr/local/include/atclient -install( - DIRECTORY ${atclient_include_dir}/${PROJECT_NAME} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) + install( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE ${PROJECT_NAME}-targets.cmake # New: Required when writing our own -config.cmake file + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) -# ######################################################### -# 5. Export in case people use us in a subdirectory -# ######################################################### -export( - EXPORT ${PROJECT_NAME}-config - NAMESPACE ${PROJECT_NAME}:: - FILE ${PROJECT_NAME}-config.cmake -) + # put everything inside of include/atclient into /usr/local/include/atclient + install( + DIRECTORY ${atclient_include_dir}/${PROJECT_NAME} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) -export( - EXPORT ${PROJECT_NAME}-config - NAMESPACE ${PROJECT_NAME}:: - FILE ${PROJECT_NAME}-targets.cmake -) + # ######################################################### + # 5. Export in case people use us in a subdirectory + # ######################################################### + export( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE ${PROJECT_NAME}-config.cmake + ) -export( - PACKAGE ${PROJECT_NAME} -) + export( + EXPORT ${PROJECT_NAME}-config + NAMESPACE ${PROJECT_NAME}:: + FILE ${PROJECT_NAME}-targets.cmake + ) -add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + export( + PACKAGE ${PROJECT_NAME} + ) -# ######################################################### -# 6. Add tests -# Run the tests via cd build && make all && cd tests && ctest -# configure with -DATCLIENT_BUILD_TESTS=ON -# ######################################################### -if(ATCLIENT_BUILD_TESTS) - enable_testing() - add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tests) + add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + + # ######################################################### + # 6. Add tests + # Run the tests via cd build && make all && cd tests && ctest + # configure with -DATCLIENT_BUILD_TESTS=ON + # ######################################################### + if(ATCLIENT_BUILD_TESTS) + enable_testing() + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tests) + endif() + + # ######################################################### + # 7. Build for ESP32 + # ######################################################### + if(ATCLIENT_BUILD_ESPIDF) + message(STATUS "Building for ESP32") + set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src) + include($ENV{IDF_PATH}/tools/cmake/project.cmake) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src) + endif() endif() - -# ######################################################### -# ######################################################### -# ######################################################### -# Old CMakeLists.txt -# ######################################################### -# ######################################################### -# ######################################################### - -# cmake_minimum_required(VERSION 3.19) - -# option(BUILD_ESP_IDF "Build for ESP-IDF" OFF) -# option(BUILD_ARDUINO "Build for Arduino" OFF) -# option(BUILD_MBEDTLS "Build for mbedTLS" OFF) - -# if(BUILD_ESP_IDF) # build as an ESP-IDF components -# add_compile_definitions(BUILD_ESP_IDF) -# message(STATUS "BUILDING FOR ESP-IDF") -# set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/targets/esp32_espidf) -# set(COMPONENTS at_client at_chops esp32_espidf) -# include($ENV{IDF_PATH}/tools/cmake/project.cmake) -# endif() - -# project( -# at_client -# VERSION 1.0.0 -# DESCRIPTION "The at_client implemented in C" -# HOMEPAGE_URL https://atsign.com -# LANGUAGES C -# ) - -# if(NOT BUILD_ESP_IDF) # build for other platforms (Arduino or MbedTLS) - -# # build libraries - -# if(BUILD_MBEDTLS) -# add_compile_definitions(BUILD_MBEDTLS) -# message(STATUS "BUILDING FOR MBEDTLS") -# add_subdirectory(deps/mbedtls) -# endif() - -# if(BUILD_ARDUINO) -# add_compile_definitions(BUILD_ARDUINO) -# message(STATUS "BUILDING FOR ARDUINO") -# include(${CMAKE_CURRENT_SOURCE_DIR}/deps/Arduino-CMake-Toolchain/Arduino-toolchain.cmake) -# endif() - -# # setup at_client library - -# FILE(GLOB_RECURSE at_client_sources ${CMAKE_SOURCE_DIR}/src/at_client/*.*) -# add_library(at_client STATIC ${at_client_sources}) - -# # setup at_chops library -# FILE(GLOB_RECURSE at_chops_sources ${CMAKE_SOURCE_DIR}/src/at_chops/*.*) -# add_library(at_chops STATIC ${at_chops_sources}) - -# set_target_properties(at_client PROPERTIES LINKER_LANGUAGE C) -# set(CMAKE_C_STANDARD 99) -# target_compile_features(at_client PRIVATE c_std_99) - -# target_include_directories(at_chops PUBLIC ${CMAKE_SOURCE_DIR}/include) -# target_include_directories(at_chops PUBLIC ${CMAKE_SOURCE_DIR}/include/at_chops) -# target_include_directories(at_client PUBLIC ${CMAKE_SOURCE_DIR}/include) -# target_include_directories(at_client PUBLIC ${CMAKE_SOURCE_DIR}/include/at_client) - -# # link libraries -# if(BUILD_MBEDTLS) -# target_link_libraries(at_chops PUBLIC mbedtls) -# target_link_libraries(at_chops PUBLIC mbedx509) -# target_link_libraries(at_chops PUBLIC mbedcrypto) -# endif() - -# if(BUILD_ARDUINO) -# target_link_arduino_libraries(at_chops PRIVATE core) -# endif() - -# target_link_libraries(at_client PRIVATE at_chops) - -# # tests -# # enable_testing() - -# # add_executable(test_base64 test/test_base64.c) -# # target_link_libraries(test_base64 PRIVATE at_client) -# # add_test( -# # NAME BASE64 -# # COMMAND $) - -# # add_executable(test_aes_ctr test/test_aes_ctr.c) -# # target_link_libraries(test_aes_ctr PRIVATE at_client) -# # add_test( -# # NAME AES_CTR -# # COMMAND $ -# # ) - -# # add_executable(test_rsapublicpopulate test/test_rsapublicpopulate.c) -# # target_link_libraries(test_rsapublicpopulate PRIVATE at_client) -# # add_test( -# # NAME RSA_PUBLICPOPULATE -# # COMMAND $) - -# # add_executable(test_rsaprivatepopulate test/test_rsaprivatepopulate.c) -# # target_link_libraries(test_rsaprivatepopulate PRIVATE at_client) -# # add_test( -# # NAME RSA_PRIVATEPOPULATE -# # COMMAND $) - -# # add_executable(test_rsasign test/test_rsasign.c) -# # target_link_libraries(test_rsasign PRIVATE at_client) -# # add_test( -# # NAME RSA_SIGN -# # COMMAND $) - -# # add_executable(test_rsaencrypt test/test_rsaencrypt.c) -# # target_link_libraries(test_rsaencrypt PRIVATE at_client) -# # add_test( -# # NAME RSA_ENCRYPT -# # COMMAND $) - -# # add_executable(test_rsadecrypt test/test_rsadecrypt.c) -# # target_link_libraries(test_rsadecrypt PRIVATE at_client) -# # add_test( -# # NAME RSA_DECRYPT -# # COMMAND $) - -# # add_executable(test_connection test/test_connection.c) -# # target_link_libraries(test_connection PRIVATE at_client) -# # add_test( -# # NAME CONNECTION -# # COMMAND $) - -# # compiler flags -# target_compile_options(at_client # https://vladiant.github.io/blog/2021/08/14/cpp-compiler-flags -# PRIVATE -Wall -Wextra -Wuninitialized -Wpedantic -Wshadow -Wmissing-include-dirs -Wundef -Winvalid-pch # Essentials -# PRIVATE -Winit-self -Wswitch-enum -Wswitch-default -Wformat=2 -Wformat-nonliteral -Wformat-security -Wformat-y2k # Control flow -# PRIVATE -Wdouble-promotion -Wfloat-equal -Wpointer-arith # Arithmetic -# PRIVATE -Wstrict-overflow=5 -Wcast-qual -Wcast-align -Wconversion -Wpacked # Casting -# PRIVATE -Wstrict-aliasing -fstrict-aliasing -Wredundant-decls -Wmissing-declarations -Wmissing-field-initializers # Sanitizing -# PRIVATE -Wwrite-strings -Wstack-protector -fstack-protector -Wpadded -Winline -Wdisabled-optimization # Security -# PRIVATE -Waggregate-return -Wbad-function-cast -Wc++-compat # C specific -# ) -# endif() diff --git a/packages/atclient/install-system.sh b/packages/atclient/install-system.sh index 4a9ad8a0..e4a477dd 100755 --- a/packages/atclient/install-system.sh +++ b/packages/atclient/install-system.sh @@ -1,5 +1,5 @@ #!/bin/bash set -eu sudo rm -f build/CMakeCache.txt -cmake -S . -B build -sudo cmake --build build --target install \ No newline at end of file +cmake -S . -B build -DATCLIENT_FETCH_ATCHOPS=ON -DATCLIENT_FETCH_MBEDTLS=ON +sudo cmake --build build --target install diff --git a/packages/atclient/run-tests.sh b/packages/atclient/run-tests.sh index af779690..11cd780b 100755 --- a/packages/atclient/run-tests.sh +++ b/packages/atclient/run-tests.sh @@ -1,6 +1,6 @@ #!/bin/bash set -eu -cmake -S . -B build -DATCLIENT_BUILD_TESTS=ON +cmake -S . -B build -DATCLIENT_BUILD_TESTS=ON -DATCLIENT_FETCH_MBEDTLS=OFF -DATCLIENT_FETCH_ATCHOPS=OFF sudo cmake --build build --target all cd build/tests ctest --output-on-failure diff --git a/packages/atclient/src/CMakeLists.txt b/packages/atclient/src/CMakeLists.txt deleted file mode 100644 index 8c5e6bb2..00000000 --- a/packages/atclient/src/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -if(ESP_PLATFORM) - message(STATUS "BUILDING AT_CLIENT COMPONENT") - - FILE(GLOB_RECURSE sources ${CMAKE_CURRENT_LIST_DIR}/*.*) - - idf_component_register( - SRCS ${sources} - INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/include - REQUIRES mbedtls - ) - - add_custom_command( - TARGET ${COMPONENT_LIB} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/lib/esp32_espidf/lib${COMPONENT_NAME}.a - COMMENT "Copying built archive file and header to lib/esp32_espidf directory..." - ) - -endif() - diff --git a/packages/atclient/src/connection.c b/packages/atclient/src/connection.c index 16374a9d..95047c3e 100644 --- a/packages/atclient/src/connection.c +++ b/packages/atclient/src/connection.c @@ -52,7 +52,7 @@ int atclient_connection_connect(atclient_connection_ctx *ctx, const char *host, } // printf("mbedtls_ctr_drbg_seed: %d\n", ret); - ret = mbedtls_x509_crt_parse(ctx->cacert, ROOT_CERT, strlen(ROOT_CERT) + 1); + ret = mbedtls_x509_crt_parse(ctx->cacert, (const unsigned char *) ROOT_CERT, strlen(ROOT_CERT) + 1); if(ret != 0) { goto exit; @@ -133,7 +133,7 @@ int atclient_connection_connect(atclient_connection_ctx *ctx, const char *host, } // printf("mbedtls_ssl_read: %d\n", ret); - mbedtls_ssl_write(ctx->ssl, "\n", 1); + mbedtls_ssl_write(ctx->ssl, (const unsigned char *) "\n", 1); ret = mbedtls_ssl_read(ctx->ssl, readbuf, readbuflen); if(ret < 0) diff --git a/packages/atclient/targets/esp32_espidf/CMakeLists.txt b/packages/atclient/targets/esp32_espidf/CMakeLists.txt deleted file mode 100644 index 630a93cb..00000000 --- a/packages/atclient/targets/esp32_espidf/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -idf_component_register( - SRCS "main.c" - INCLUDE_DIRS "." - # All components & dependencies to be built: - REQUIRES at_client mbedtls -) diff --git a/packages/atclient_esp32/CMakeLists.txt b/packages/atclient_esp32/CMakeLists.txt new file mode 100644 index 00000000..88111319 --- /dev/null +++ b/packages/atclient_esp32/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.19) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_SOURCE_DIR}/../atclient ${CMAKE_SOURCE_DIR}/../atchops) +set(COMPONENTS atclient atchops main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(atclient_esp32) \ No newline at end of file diff --git a/packages/atclient_esp32/include/atchops/aes_ctr.h b/packages/atclient_esp32/include/atchops/aes_ctr.h new file mode 100644 index 00000000..ca9eb607 --- /dev/null +++ b/packages/atclient_esp32/include/atchops/aes_ctr.h @@ -0,0 +1,31 @@ +#pragma once + +typedef enum AESKeySize { + AES_128 = 128, // not tested + AES_192 = 192, // not tested + AES_256 = 256, +} AESKeySize; + +int atchops_aes_ctr_encrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned long *ciphertextbase64olen); + +int atchops_aes_ctr_decrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned long *plaintextolen); diff --git a/packages/atclient_esp32/include/atchops/base64.h b/packages/atclient_esp32/include/atchops/base64.h new file mode 100644 index 00000000..d1a9c227 --- /dev/null +++ b/packages/atclient_esp32/include/atchops/base64.h @@ -0,0 +1,25 @@ +#pragma once + +/** + * @brief Base64 encode some bytes + * + * @param src src bytes that you want to encode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 encoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_encode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); + +/** + * @brief Base64 decode some bytes + * + * @param src src bytes that you want to decode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 decoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_decode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); diff --git a/packages/atclient_esp32/include/atchops/rsa.h b/packages/atclient_esp32/include/atchops/rsa.h new file mode 100644 index 00000000..bffb64b4 --- /dev/null +++ b/packages/atclient_esp32/include/atchops/rsa.h @@ -0,0 +1,84 @@ +#pragma once + +#include "sha.h" + +typedef struct rsa_param +{ + unsigned long len; // length of the number in bytes + unsigned char *value; // hex byte array of the number +} rsa_param; + +typedef struct atchops_rsa_publickey +{ + rsa_param n; // modulus + rsa_param e; // public exponent +} atchops_rsa_publickey; + +typedef struct atchops_rsa_privatekey +{ + rsa_param n; // modulus + rsa_param e; // public exponent + rsa_param d; // private exponent + rsa_param p; // prime 1 + rsa_param q; // prime 2 +} atchops_rsa_privatekey; + +/** + * @brief Populate a public key struct from a base64 string + * + * @param publickeybase64 a base64 string representing an RSA 2048 Public Key + * @param publickeybase64len the length of the base64 string + * @param publickeystruct the public key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_publickey(const char *publickeybase64, const unsigned long publickeybase64len, atchops_rsa_publickey *publickeystruct); + +/** + * @brief Populate a private key struct from a base64 string + * + * @param privatekeybase64 the base64 string representing an RSA 2048 Private Key + * @param privatekeybase64len the length of the base64 string + * @param privatekeystruct the private key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_privatekey(const char *privatekeybase64, const unsigned long privatekeybase64len, atchops_rsa_privatekey *privatekeystruct); + +/** + * @brief Sign a message with an RSA private key + * + * @param privatekeystruct the private key struct to use for signing, see atchops_rsa_populate_privatekey + * @param mdtype the hash type to use, see atchops_md_type, e.g. ATCHOPS_MD_SHA256 + * @param message the message to sign + * @param messagelen the length of the message, most people use strlen() to find this length + * @param signature the signature buffer to populate + * @param signaturelen the length of the signature buffer + * @param signatureolen the length of the signature buffer after signing + * @return int 0 on success + */ +int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, const unsigned char *message, const unsigned long messagelen, unsigned char *signature, const unsigned long signaturelen, unsigned long *signatureolen); + +/** + * @brief Encrypt bytes with an RSA public key + * + * @param publickeystruct the public key struct to use for encryption, see atchops_rsa_populate_publickey + * @param plaintext the plaintext to encrypt + * @param plaintextlen the length of the plaintext, most people use strlen() to find this length + * @param ciphertext the ciphertext buffer to populate + * @param ciphertextlen the length of the ciphertext buffer + * @param ciphertextolen the length of the ciphertext buffer after encryption + * @return int 0 on success + */ +int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned char *plaintext, const unsigned long plaintextlen, unsigned char *ciphertext, const unsigned long ciphertextlen, unsigned long *ciphertextolen); + +/** + * @brief Decrypt bytes with an RSA private key + * + * @param privatekeystruct the private key struct to use for decryption, see atchops_rsa_populate_privatekey + * @param ciphertextbase64 the ciphertext to decrypt, base64 encoded + * @param ciphertextbase64len the length of the ciphertext, most people use strlen() to find this length + * @param plaintext the plaintext buffer to populate + * @param plaintextlen the length of the plaintext buffer + * @param plaintextolen the length of the plaintext buffer after decryption + * @return int 0 on success + */ +int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, const unsigned long plaintextlen, unsigned long *plaintextolen); diff --git a/packages/atclient_esp32/include/atchops/sha.h b/packages/atclient_esp32/include/atchops/sha.h new file mode 100644 index 00000000..786522e5 --- /dev/null +++ b/packages/atclient_esp32/include/atchops/sha.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + ATCHOPS_MD_NONE=0, /**< None. */ + ATCHOPS_MD_MD5, /**< The MD5 message digest. */ + ATCHOPS_MD_SHA1, /**< The SHA-1 message digest. */ + ATCHOPS_MD_SHA224, /**< The SHA-224 message digest. */ + ATCHOPS_MD_SHA256, /**< The SHA-256 message digest. */ + ATCHOPS_MD_SHA384, /**< The SHA-384 message digest. */ + ATCHOPS_MD_SHA512, /**< The SHA-512 message digest. */ + ATCHOPS_MD_RIPEMD160, +} atchops_md_type; + +int atchops_sha_hash(atchops_md_type mdtype, const unsigned char *input, const unsigned long inputlen, unsigned char *output, unsigned long outputlen, unsigned long *outputolen); diff --git a/packages/atclient_esp32/include/atclient/at_logger.h b/packages/atclient_esp32/include/atclient/at_logger.h new file mode 100644 index 00000000..c6be04fc --- /dev/null +++ b/packages/atclient_esp32/include/atclient/at_logger.h @@ -0,0 +1,9 @@ +#ifndef ATLOGGER_H +#define ATLOGGER_H + +#include + +int atlogger_log(const char *title, const char *message); +int atlogger_logx(const char *title, const unsigned char *bytes, size_t byteslen); + +#endif // ATLOGGER_H diff --git a/packages/atclient_esp32/include/atclient/atkeys_filereader.h b/packages/atclient_esp32/include/atclient/atkeys_filereader.h new file mode 100644 index 00000000..e02fba54 --- /dev/null +++ b/packages/atclient_esp32/include/atclient/atkeys_filereader.h @@ -0,0 +1,59 @@ +#pragma once + +#define TOKEN_AES_PKAM_PUBLIC_KEY "aesPkamPublicKey" +#define TOKEN_AES_PKAM_PUBLIC_KEY_LEN 16 + +#define TOKEN_AES_PKAM_PRIVATE_KEY "aesPkamPrivateKey" +#define TOKEN_AES_PKAM_PRIVATE_KEY_LEN 17 + +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY "aesEncryptPublicKey" +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY_LEN 19 + +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY "aesEncryptPrivateKey" +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY_LEN 20 + +#define TOKEN_SELF_ENCRYPTION_KEY "selfEncryptionKey" +#define TOKEN_SELF_ENCRYPTION_KEY_LEN 17 + +typedef struct atclient_atkeysfile_entry{ + size_t len; + char *key; +} atclient_atkeysfile_entry; + +typedef struct atclient_atkeysfile { + atclient_atkeysfile_entry *aes_pkam_public_key; + atclient_atkeysfile_entry *aes_pkam_private_key; + atclient_atkeysfile_entry *aes_encrypt_public_key; + atclient_atkeysfile_entry *aes_encrypt_private_key; + atclient_atkeysfile_entry *self_encryption_key; +} atclient_atkeysfile; + +void atclient_atkeysfile_init(atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_read(const char *path, atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_write(const char *path, const char *atsign, atclient_atkeysfile *atkeysfile); +void atclient_atkeysfile_free(atclient_atkeysfile *atkeysfile); + +/** + * Usage example + * atclient_atkeysfile atkeysfile; + * atclient_atkeysfile_init(&atkeysfile); + * printf("done init...\n") + * + ret = atclient_atkeysfile_read(path, &atkeysfile); + if (ret != 0) + { + goto exit; + } + + printf("done read...\n"); + printf("aes_pkam_public_key: %s\n", atkeysfile.aes_pkam_public_key->key); + printf("aes_pkam_private_key: %s\n", atkeysfile.aes_pkam_private_key->key); + printf("aes_encrypt_public_key: %s\n", atkeysfile.aes_encrypt_public_key->key); + printf("aes_encrypt_private_key: %s\n", atkeysfile.aes_encrypt_private_key->key); + printf("self_encryption_key: %s\n", atkeysfile.self_encryption_key->key); + + printf("writing...\n"); + + ret = atclient_atkeysfile_write("/Users/jeremytubongbanua/.atsign/temp/@smoothalligator_key.atKeys", ATSIGN, &atkeysfile); + * + */ \ No newline at end of file diff --git a/packages/atclient_esp32/include/atclient/connection.h b/packages/atclient_esp32/include/atclient/connection.h new file mode 100644 index 00000000..4d0d2351 --- /dev/null +++ b/packages/atclient_esp32/include/atclient/connection.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#define HOST "root.atsign.org" +#define PORT 64 + +// #define HOST "245b44d4-a4bd-5f33-b077-c559f956486a.swarm0001.atsign.zone" +// #define PORT 1722 + +#define ROOT_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" \ + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \ + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" \ + "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" \ + "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" \ + "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" \ + "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" \ + "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" \ + "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" \ + "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" \ + "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" \ + "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" \ + "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" \ + "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" \ + "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" \ + "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" \ + "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" \ + "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" \ + "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" \ + "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" \ + "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" \ + "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" \ + "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" \ + "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" \ + "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" \ + "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" \ + "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" \ + "nLRbwHOoq7hHwg==\n" \ + "-----END CERTIFICATE-----\n" + +typedef struct atclient_connection_ctx { + char *host; // assume null terminated, example: "root.atsign.org" + int port; // example: 64 + char *cert_pem; // assume null terminated, example: "-----BEGIN CERTIFICATE-----\nMIIF..." + void *server_fd; + void *ssl; + void *conf; + void *cacert; + void *entropy; + void *ctr_drbg; + void *saved_session; +} atclient_connection_ctx; + +void atclient_connection_init(atclient_connection_ctx *ctx); +int atclient_connection_connect(atclient_connection_ctx *ctx, const char *host, const int port); +int atclient_connection_send(atclient_connection_ctx *ctx, unsigned char *recv, const size_t recvlen, size_t *olen, const unsigned char *src, const size_t srclen); +void atclient_connection_free(atclient_connection_ctx *ctx); diff --git a/packages/atclient_esp32/lib/libatchops.a b/packages/atclient_esp32/lib/libatchops.a new file mode 100644 index 0000000000000000000000000000000000000000..18a425f5d7dfea4dc6ecda54a198b42f0acce83a GIT binary patch literal 74662 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 literal 0 HcmV?d00001 diff --git a/packages/atclient_esp32/lib/libatclient.a b/packages/atclient_esp32/lib/libatclient.a new file mode 100644 index 0000000000000000000000000000000000000000..ca399ce6d856d737e969a5d4ad1a96aecbad36d7 GIT binary patch literal 81616 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 I?n(6iFNn>)%m4rY literal 0 HcmV?d00001 diff --git a/packages/atclient_esp32/main/CMakeLists.txt b/packages/atclient_esp32/main/CMakeLists.txt new file mode 100644 index 00000000..a757336b --- /dev/null +++ b/packages/atclient_esp32/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "main.c") \ No newline at end of file diff --git a/packages/atclient/targets/esp32_espidf/main.c b/packages/atclient_esp32/main/main.c similarity index 78% rename from packages/atclient/targets/esp32_espidf/main.c rename to packages/atclient_esp32/main/main.c index cb058515..f6fde2d1 100644 --- a/packages/atclient/targets/esp32_espidf/main.c +++ b/packages/atclient_esp32/main/main.c @@ -1,3 +1,4 @@ void app_main(void) { -} + +} \ No newline at end of file diff --git a/packages/repl/CMakeLists.txt b/packages/repl/CMakeLists.txt index 6ecac908..bc019efc 100644 --- a/packages/repl/CMakeLists.txt +++ b/packages/repl/CMakeLists.txt @@ -10,11 +10,12 @@ project( include(GNUInstallDirs) include(FetchContent) -FetchContent_Declare( - atclient - SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../atclient -) -FetchContent_MakeAvailable(atclient) +# FetchContent_Declare( +# atclient +# SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../atclient +# ) +# FetchContent_MakeAvailable(atclient) +find_package(atclient REQUIRED CONFIG) message(STATUS "atclient was found") diff --git a/packages/repl/run.sh b/packages/repl/run.sh index 63555895..f8949651 100755 --- a/packages/repl/run.sh +++ b/packages/repl/run.sh @@ -1,11 +1,6 @@ #!/bin/bash set -eu -# install atclient locally -cd ../atclient -./install-system.sh -cd ../repl - # clean rm -f build/CMakeCache.txt sudo rm -f bin/repl From 6265380759b979bcd866751e1477b8ac8cef648c Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 17 Aug 2023 23:58:06 -0400 Subject: [PATCH 13/26] refactor: atclient_espidf --- .../CMakeLists.txt | 0 .../include/atchops/aes_ctr.h | 0 .../include/atchops/base64.h | 0 .../include/atchops/rsa.h | 0 .../include/atchops/sha.h | 0 .../include/atclient/at_logger.h | 0 .../include/atclient/atkeys_filereader.h | 0 .../include/atclient/connection.h | 0 .../lib/libatchops.a | Bin .../lib/libatclient.a | Bin .../main/CMakeLists.txt | 0 .../{atclient_esp32 => atclient_espidf}/main/main.c | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename packages/{atclient_esp32 => atclient_espidf}/CMakeLists.txt (100%) rename packages/{atclient_esp32 => atclient_espidf}/include/atchops/aes_ctr.h (100%) rename packages/{atclient_esp32 => atclient_espidf}/include/atchops/base64.h (100%) rename packages/{atclient_esp32 => atclient_espidf}/include/atchops/rsa.h (100%) rename packages/{atclient_esp32 => atclient_espidf}/include/atchops/sha.h (100%) rename packages/{atclient_esp32 => atclient_espidf}/include/atclient/at_logger.h (100%) rename packages/{atclient_esp32 => atclient_espidf}/include/atclient/atkeys_filereader.h (100%) rename packages/{atclient_esp32 => atclient_espidf}/include/atclient/connection.h (100%) rename packages/{atclient_esp32 => atclient_espidf}/lib/libatchops.a (100%) rename packages/{atclient_esp32 => atclient_espidf}/lib/libatclient.a (100%) rename packages/{atclient_esp32 => atclient_espidf}/main/CMakeLists.txt (100%) rename packages/{atclient_esp32 => atclient_espidf}/main/main.c (100%) diff --git a/packages/atclient_esp32/CMakeLists.txt b/packages/atclient_espidf/CMakeLists.txt similarity index 100% rename from packages/atclient_esp32/CMakeLists.txt rename to packages/atclient_espidf/CMakeLists.txt diff --git a/packages/atclient_esp32/include/atchops/aes_ctr.h b/packages/atclient_espidf/include/atchops/aes_ctr.h similarity index 100% rename from packages/atclient_esp32/include/atchops/aes_ctr.h rename to packages/atclient_espidf/include/atchops/aes_ctr.h diff --git a/packages/atclient_esp32/include/atchops/base64.h b/packages/atclient_espidf/include/atchops/base64.h similarity index 100% rename from packages/atclient_esp32/include/atchops/base64.h rename to packages/atclient_espidf/include/atchops/base64.h diff --git a/packages/atclient_esp32/include/atchops/rsa.h b/packages/atclient_espidf/include/atchops/rsa.h similarity index 100% rename from packages/atclient_esp32/include/atchops/rsa.h rename to packages/atclient_espidf/include/atchops/rsa.h diff --git a/packages/atclient_esp32/include/atchops/sha.h b/packages/atclient_espidf/include/atchops/sha.h similarity index 100% rename from packages/atclient_esp32/include/atchops/sha.h rename to packages/atclient_espidf/include/atchops/sha.h diff --git a/packages/atclient_esp32/include/atclient/at_logger.h b/packages/atclient_espidf/include/atclient/at_logger.h similarity index 100% rename from packages/atclient_esp32/include/atclient/at_logger.h rename to packages/atclient_espidf/include/atclient/at_logger.h diff --git a/packages/atclient_esp32/include/atclient/atkeys_filereader.h b/packages/atclient_espidf/include/atclient/atkeys_filereader.h similarity index 100% rename from packages/atclient_esp32/include/atclient/atkeys_filereader.h rename to packages/atclient_espidf/include/atclient/atkeys_filereader.h diff --git a/packages/atclient_esp32/include/atclient/connection.h b/packages/atclient_espidf/include/atclient/connection.h similarity index 100% rename from packages/atclient_esp32/include/atclient/connection.h rename to packages/atclient_espidf/include/atclient/connection.h diff --git a/packages/atclient_esp32/lib/libatchops.a b/packages/atclient_espidf/lib/libatchops.a similarity index 100% rename from packages/atclient_esp32/lib/libatchops.a rename to packages/atclient_espidf/lib/libatchops.a diff --git a/packages/atclient_esp32/lib/libatclient.a b/packages/atclient_espidf/lib/libatclient.a similarity index 100% rename from packages/atclient_esp32/lib/libatclient.a rename to packages/atclient_espidf/lib/libatclient.a diff --git a/packages/atclient_esp32/main/CMakeLists.txt b/packages/atclient_espidf/main/CMakeLists.txt similarity index 100% rename from packages/atclient_esp32/main/CMakeLists.txt rename to packages/atclient_espidf/main/CMakeLists.txt diff --git a/packages/atclient_esp32/main/main.c b/packages/atclient_espidf/main/main.c similarity index 100% rename from packages/atclient_esp32/main/main.c rename to packages/atclient_espidf/main/main.c From b043da938d6cfe5b7765c9beff8444399c8705d5 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Fri, 18 Aug 2023 00:03:34 -0400 Subject: [PATCH 14/26] refactor: spacing --- packages/atclient_espidf/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/atclient_espidf/CMakeLists.txt b/packages/atclient_espidf/CMakeLists.txt index 88111319..b91a74a5 100644 --- a/packages/atclient_espidf/CMakeLists.txt +++ b/packages/atclient_espidf/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.19) -set(EXTRA_COMPONENT_DIRS ${CMAKE_SOURCE_DIR}/../atclient ${CMAKE_SOURCE_DIR}/../atchops) +set(EXTRA_COMPONENT_DIRS + ${CMAKE_SOURCE_DIR}/../atclient + ${CMAKE_SOURCE_DIR}/../atchops +) + set(COMPONENTS atclient atchops main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) From e92cae94c5d716e8a306200b1d732dddf130607b Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Fri, 18 Aug 2023 21:15:17 -0400 Subject: [PATCH 15/26] refactor: examples folder --- .vscode/c_cpp_properties.json | 3 +- .../atclient_esp32}/CMakeLists.txt | 6 +- .../atclient_esp32}/include/atchops/aes_ctr.h | 0 .../atclient_esp32}/include/atchops/base64.h | 0 .../atclient_esp32}/include/atchops/rsa.h | 0 .../atclient_esp32}/include/atchops/sha.h | 0 .../include/atclient/at_logger.h | 0 .../include/atclient/atkeys_filereader.h | 0 .../include/atclient/connection.h | 0 .../atclient_esp32}/lib/libatchops.a | Bin 74662 -> 74662 bytes .../atclient_esp32}/lib/libatclient.a | Bin 81616 -> 81616 bytes examples/atclient_esp32/main/CMakeLists.txt | 4 + examples/atclient_esp32/main/main.c | 74 ++++++++++++++++++ {packages => examples}/repl/CMakeLists.txt | 2 +- {packages => examples}/repl/run.sh | 0 {packages => examples}/repl/src/main.c | 0 packages/atclient_espidf/main/CMakeLists.txt | 1 - packages/atclient_espidf/main/main.c | 4 - 18 files changed, 83 insertions(+), 11 deletions(-) rename {packages/atclient_espidf => examples/atclient_esp32}/CMakeLists.txt (56%) rename {packages/atclient_espidf => examples/atclient_esp32}/include/atchops/aes_ctr.h (100%) rename {packages/atclient_espidf => examples/atclient_esp32}/include/atchops/base64.h (100%) rename {packages/atclient_espidf => examples/atclient_esp32}/include/atchops/rsa.h (100%) rename {packages/atclient_espidf => examples/atclient_esp32}/include/atchops/sha.h (100%) rename {packages/atclient_espidf => examples/atclient_esp32}/include/atclient/at_logger.h (100%) rename {packages/atclient_espidf => examples/atclient_esp32}/include/atclient/atkeys_filereader.h (100%) rename {packages/atclient_espidf => examples/atclient_esp32}/include/atclient/connection.h (100%) rename {packages/atclient_espidf => examples/atclient_esp32}/lib/libatchops.a (69%) rename {packages/atclient_espidf => examples/atclient_esp32}/lib/libatclient.a (79%) create mode 100644 examples/atclient_esp32/main/CMakeLists.txt create mode 100644 examples/atclient_esp32/main/main.c rename {packages => examples}/repl/CMakeLists.txt (92%) rename {packages => examples}/repl/run.sh (100%) rename {packages => examples}/repl/src/main.c (100%) delete mode 100644 packages/atclient_espidf/main/CMakeLists.txt delete mode 100644 packages/atclient_espidf/main/main.c diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index ef8bfd76..975b9886 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -6,7 +6,8 @@ "includePath": [ "${default}", "${workspaceFolder}/**", - "/usr/local/include" + "/usr/local/include", + "/Users/jeremytubongbanua/esp/esp-idf/components/**" ], "compilerPath": "/usr/bin/gcc", "cStandard": "c99", diff --git a/packages/atclient_espidf/CMakeLists.txt b/examples/atclient_esp32/CMakeLists.txt similarity index 56% rename from packages/atclient_espidf/CMakeLists.txt rename to examples/atclient_esp32/CMakeLists.txt index b91a74a5..e98bb22e 100644 --- a/packages/atclient_espidf/CMakeLists.txt +++ b/examples/atclient_esp32/CMakeLists.txt @@ -1,12 +1,10 @@ cmake_minimum_required(VERSION 3.19) set(EXTRA_COMPONENT_DIRS - ${CMAKE_SOURCE_DIR}/../atclient - ${CMAKE_SOURCE_DIR}/../atchops + ${CMAKE_SOURCE_DIR}/../../packages/atclient + ${CMAKE_SOURCE_DIR}/../../packages/atchops ) -set(COMPONENTS atclient atchops main) - include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(atclient_esp32) \ No newline at end of file diff --git a/packages/atclient_espidf/include/atchops/aes_ctr.h b/examples/atclient_esp32/include/atchops/aes_ctr.h similarity index 100% rename from packages/atclient_espidf/include/atchops/aes_ctr.h rename to examples/atclient_esp32/include/atchops/aes_ctr.h diff --git a/packages/atclient_espidf/include/atchops/base64.h b/examples/atclient_esp32/include/atchops/base64.h similarity index 100% rename from packages/atclient_espidf/include/atchops/base64.h rename to examples/atclient_esp32/include/atchops/base64.h diff --git a/packages/atclient_espidf/include/atchops/rsa.h b/examples/atclient_esp32/include/atchops/rsa.h similarity index 100% rename from packages/atclient_espidf/include/atchops/rsa.h rename to examples/atclient_esp32/include/atchops/rsa.h diff --git a/packages/atclient_espidf/include/atchops/sha.h b/examples/atclient_esp32/include/atchops/sha.h similarity index 100% rename from packages/atclient_espidf/include/atchops/sha.h rename to examples/atclient_esp32/include/atchops/sha.h diff --git a/packages/atclient_espidf/include/atclient/at_logger.h b/examples/atclient_esp32/include/atclient/at_logger.h similarity index 100% rename from packages/atclient_espidf/include/atclient/at_logger.h rename to examples/atclient_esp32/include/atclient/at_logger.h diff --git a/packages/atclient_espidf/include/atclient/atkeys_filereader.h b/examples/atclient_esp32/include/atclient/atkeys_filereader.h similarity index 100% rename from packages/atclient_espidf/include/atclient/atkeys_filereader.h rename to examples/atclient_esp32/include/atclient/atkeys_filereader.h diff --git a/packages/atclient_espidf/include/atclient/connection.h b/examples/atclient_esp32/include/atclient/connection.h similarity index 100% rename from packages/atclient_espidf/include/atclient/connection.h rename to examples/atclient_esp32/include/atclient/connection.h diff --git a/packages/atclient_espidf/lib/libatchops.a b/examples/atclient_esp32/lib/libatchops.a similarity index 69% rename from packages/atclient_espidf/lib/libatchops.a rename to examples/atclient_esp32/lib/libatchops.a index 18a425f5d7dfea4dc6ecda54a198b42f0acce83a..c32da5492bf0c27ea82cbe5d99e25d7378dbc1d6 100644 GIT binary patch delta 3643 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 WrKDI3FH>V1vZ$P{i}htu&%Xf)>%|`c delta 3604 zcmZWs3v?9K8J>S0JCm86-3{41cHIP$H7_6`fCPzPXrM$vNI*ayDqs>GH6$SqlW1gl zXh0D$UhP6XNKwQf(Wr|?K_H%LMXfIs#rneb2wEz5XrUEF`u!8I_H@p<_x}I)zyG^; z=I-{_)a|dSPoL2u(-cW^A0SB@hVjcsCf6!Jii$}dlyW&_G8v8FREL(eG=^k-jf_Rr z{>hxG{|=lwn`uLP+gbE&u+da)Nll}qHuP!jVb3|BMUyK)C1QiCfW8;Ku90J3hrO1s zya}1|Z_uvvfE&u*Flb$k0M-@HL?FI=RgzZtUIyM9e+g!ZBqc11k$N`WE9U8?6cUf? zb7-NEjm0!t+-wxnrDCTMAVs`tWYd-6Tcaqm6o#}cxET)jz(o&6uz|7qz+IR13fX(e@ve0IKGNqo$5DD8d-fhi z$jKnr-HvEqgFnhxmq_)@rEg-bo-MNGMmlyZHNMd&%$(tjBsY+p}r5D4iUZv%v_@a#?C*FhVM_{xA@=Z_^b+V<(ECN-QJSGmt4{)M3%)vP9N0SfXn8 zvP9Jf0o0lt8+{BGm+GjIu^e)pi;^d6#SrYrFxK*v$rD&{LwP_>ltfE;4$To;%kwBI z-YLJtdo3)<)7l0#nqjRL-C)%=B1qf%0oMBUaqA$LtVygq-@OU6YL!}GZi*iR6T=$AOohYvt>HoDsvK^B zHIbvP6Kp+9l0QJnZe8U@$7OkjqG-EG5w***a?YTd zxB|~wK$5k9nW3JIV0_jWtUN5C6HK}Fl-Rp$qFsl?;zlTA04W~}fBlHa{RrbXZ}we{ z0M-hao7I`GIKEy+_sykNm(!+Q%+w@u&kU;qaJryM;*%71XV%kMfc_%1 zq(wHHeH+>LohaPwYlCE+VRvn*FDMD@?=!d6`sSkzO*u0BoK;+G^2F88G zKHlJIko^{7NmfV%8wN&xM9NmavI`RB0ob_H1CF~P2zQPBgl{!4YZkJycP20`S@q0x zIs4(WD$!p1d57^^N$8Hf+xcOnSlKASe#w!9tQ{=r4G)Pg}V}xuYPs2^E9yt#oGri)p8y*%wYv92u_FYf2sP7q~?ZObS47NcqO!(Dy;3Nkl{v&&QIb&(Ru z2%lon`2dRsq$%7EiJTq39@Wjk=)XtY5XryPsebsyxEHH0ycieNf3Jz|NDigP4o9Aq zDI(Ub3M7_u6Y7Wd!uwnB#aE}CY2w9IBT&uBRgU*_uSXJ_Q8afakM9U19BePh zJ&c`VY^#GE1oog{J`=l!7S!RSk3n1c-iU%=QYMZD6^0gt!0kozD~=QoyL z$~R-RYxjjj>0|pv-ebYo)!Uzv|L-y*R(2+f?$c&0dB;xIh0kwF>`1p~GF6F_KZfO^ ztN2nQ2A=cLZ^RYnvgr{~ajuNsh{eu@W$O@3-kCoReYC4ZRA_gq!gN1(5& ztUtzYcSAeRoT2jDSS8mkXEEM@%(=>_vSzS?sUYcB9evY75AiWb_~o5mTDqXRy0$_rdpsc;@zQZV o2|aZ_+8s|-(Los$roTkXGANt+MVon<7~PUVWi%~%GJ|^l1Hgl?7ytkO diff --git a/packages/atclient_espidf/lib/libatclient.a b/examples/atclient_esp32/lib/libatclient.a similarity index 79% rename from packages/atclient_espidf/lib/libatclient.a rename to examples/atclient_esp32/lib/libatclient.a index ca399ce6d856d737e969a5d4ad1a96aecbad36d7..baef866a43fa24a363a30fc69a00d4f1b67f6d85 100644 GIT binary patch delta 3936 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 G(|-U&q$5HA delta 3898 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 A-v9sr diff --git a/examples/atclient_esp32/main/CMakeLists.txt b/examples/atclient_esp32/main/CMakeLists.txt new file mode 100644 index 00000000..709b75c8 --- /dev/null +++ b/examples/atclient_esp32/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "main.c" + REQUIRES atclient esp_wifi +) diff --git a/examples/atclient_esp32/main/main.c b/examples/atclient_esp32/main/main.c new file mode 100644 index 00000000..7caafa31 --- /dev/null +++ b/examples/atclient_esp32/main/main.c @@ -0,0 +1,74 @@ + +#include +#include +#include +#include +#include "atclient/connection.h" + +#define ATSIGN "@jeremy_0" + +static void *without_at_symbol(const char *atsign, char *buf) +{ + int i = 0; + while (atsign[i] != '\0') + { + buf[i] = atsign[i + 1]; + i++; + } + buf[i] = '\0'; + return buf; +} + +void app_main(void) +{ + esp_wifi_set_mode(WIFI_MODE_STA); + + // 1. initialize buffer to use throughout program + size_t recvlen = 32768; + unsigned char *recv = malloc(sizeof(unsigned char) * recvlen); + size_t olen = 0; + + // 2. connect to root and find secondary address + + // 2a. establish connection to root + atclient_connection_ctx root_connection; + atclient_connection_init(&root_connection); + atclient_connection_connect(&root_connection, "root.atsign.org", 64); + printf("Connected to root\n"); + + // 2b. send atsign without @ symbol to root + + char *atsign_without_at = malloc(sizeof(char) * 100); + memset(atsign_without_at, 0, 100); + without_at_symbol(ATSIGN, atsign_without_at); + strcat(atsign_without_at, "\r\n"); + size_t atsign_without_atlen = strlen(atsign_without_at); + + printf("Sending to root: \"%s\"\n", atsign_without_at); + atclient_connection_send(&root_connection, recv, recvlen, &olen, (unsigned char *)atsign_without_at, atsign_without_atlen); + printf("Received from root: \"%.*s\"\n", (int)olen, recv); + + // 2c. parse secondary address + const size_t secondary_len = 100; + char *secondary_host = malloc(sizeof(char) * secondary_len); + char *secondary_port = malloc(sizeof(char) * secondary_len); + + int i = 0, c; + while ((c = recv[i]) != ':' && i < olen) + { + secondary_host[i] = c; + i++; + } + secondary_host[i] = '\0'; + i++; + int j = 0; + while ((c = recv[i]) != '\0' && i < olen) + { + secondary_port[j] = c; + i++; + j++; + } + + printf("secondary_host: %s\n", secondary_host); + printf("secondary_port: %s\n", secondary_port); +} \ No newline at end of file diff --git a/packages/repl/CMakeLists.txt b/examples/repl/CMakeLists.txt similarity index 92% rename from packages/repl/CMakeLists.txt rename to examples/repl/CMakeLists.txt index bc019efc..977c8770 100644 --- a/packages/repl/CMakeLists.txt +++ b/examples/repl/CMakeLists.txt @@ -12,7 +12,7 @@ include(FetchContent) # FetchContent_Declare( # atclient -# SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../atclient +# SOURCE_DIR ${CMAKE_SOURCE_DIR}/../../atclient # ) # FetchContent_MakeAvailable(atclient) find_package(atclient REQUIRED CONFIG) diff --git a/packages/repl/run.sh b/examples/repl/run.sh similarity index 100% rename from packages/repl/run.sh rename to examples/repl/run.sh diff --git a/packages/repl/src/main.c b/examples/repl/src/main.c similarity index 100% rename from packages/repl/src/main.c rename to examples/repl/src/main.c diff --git a/packages/atclient_espidf/main/CMakeLists.txt b/packages/atclient_espidf/main/CMakeLists.txt deleted file mode 100644 index a757336b..00000000 --- a/packages/atclient_espidf/main/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -idf_component_register(SRCS "main.c") \ No newline at end of file diff --git a/packages/atclient_espidf/main/main.c b/packages/atclient_espidf/main/main.c deleted file mode 100644 index f6fde2d1..00000000 --- a/packages/atclient_espidf/main/main.c +++ /dev/null @@ -1,4 +0,0 @@ -void app_main(void) -{ - -} \ No newline at end of file From ea37c38de2671087f5ef0f1d762ea6bc80c778ee Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 21 Aug 2023 19:48:26 -0400 Subject: [PATCH 16/26] docs: lots of examples and docs --- README.md | 117 +----------------- examples/atclient_esp32/CMakeLists.txt | 10 -- examples/atclient_esp32/main/main.c | 74 ----------- examples/atclient_esp32_source/CMakeLists.txt | 10 ++ examples/atclient_esp32_source/README.md | 37 ++++++ .../include/atchops/aes_ctr.h | 0 .../include/atchops/base64.h | 0 .../include/atchops/rsa.h | 0 .../include/atchops/sha.h | 0 .../include/atclient/at_logger.h | 0 .../include/atclient/atkeys_filereader.h | 0 .../include/atclient/connection.h | 0 .../atclient_esp32_source/lib/libatchops.a | Bin 0 -> 74694 bytes .../atclient_esp32_source/lib/libatclient.a | Bin 0 -> 81640 bytes .../atclient_esp32_source/main/CMakeLists.txt | 4 + examples/atclient_esp32_source/main/main.c | 28 +++++ .../CMakeLists.txt | 5 + .../README.md | 91 ++++++++++++++ .../components/atchops/CMakeLists.txt | 7 ++ .../atchops/include/atchops/aes_ctr.h | 31 +++++ .../atchops/include/atchops/base64.h | 25 ++++ .../components/atchops/include/atchops/rsa.h | 84 +++++++++++++ .../components/atchops/include/atchops/sha.h | 14 +++ .../components/atchops}/lib/libatchops.a | Bin .../components/atclient/CMakeLists.txt | 7 ++ .../atclient/include/atclient/at_logger.h | 9 ++ .../include/atclient/atkeys_filereader.h | 59 +++++++++ .../atclient/include/atclient/connection.h | 59 +++++++++ .../components/atclient}/lib/libatclient.a | Bin 81616 -> 81616 bytes .../main/CMakeLists.txt | 2 +- .../main/main.c | 28 +++++ .../CMakeLists.txt | 5 + .../main/CMakeLists.txt | 11 ++ .../main/atchops/CMakeLists.txt | 7 ++ .../main/atchops/include/atchops/aes_ctr.h | 31 +++++ .../main/atchops/include/atchops/base64.h | 25 ++++ .../main/atchops/include/atchops/rsa.h | 84 +++++++++++++ .../main/atchops/include/atchops/sha.h | 14 +++ .../main/atchops/lib/libatchops.a | Bin 0 -> 74662 bytes .../main/atclient/CMakeLists.txt | 7 ++ .../atclient/include/atclient/at_logger.h | 9 ++ .../include/atclient/atkeys_filereader.h | 59 +++++++++ .../atclient/include/atclient/connection.h | 59 +++++++++ .../main/atclient/lib/libatclient.a | Bin 0 -> 81616 bytes .../main/main.c | 28 +++++ 45 files changed, 844 insertions(+), 196 deletions(-) delete mode 100644 examples/atclient_esp32/CMakeLists.txt delete mode 100644 examples/atclient_esp32/main/main.c create mode 100644 examples/atclient_esp32_source/CMakeLists.txt create mode 100644 examples/atclient_esp32_source/README.md rename examples/{atclient_esp32 => atclient_esp32_source}/include/atchops/aes_ctr.h (100%) rename examples/{atclient_esp32 => atclient_esp32_source}/include/atchops/base64.h (100%) rename examples/{atclient_esp32 => atclient_esp32_source}/include/atchops/rsa.h (100%) rename examples/{atclient_esp32 => atclient_esp32_source}/include/atchops/sha.h (100%) rename examples/{atclient_esp32 => atclient_esp32_source}/include/atclient/at_logger.h (100%) rename examples/{atclient_esp32 => atclient_esp32_source}/include/atclient/atkeys_filereader.h (100%) rename examples/{atclient_esp32 => atclient_esp32_source}/include/atclient/connection.h (100%) create mode 100644 examples/atclient_esp32_source/lib/libatchops.a create mode 100644 examples/atclient_esp32_source/lib/libatclient.a create mode 100644 examples/atclient_esp32_source/main/CMakeLists.txt create mode 100644 examples/atclient_esp32_source/main/main.c create mode 100644 examples/atclient_esp32_static_components/CMakeLists.txt create mode 100644 examples/atclient_esp32_static_components/README.md create mode 100644 examples/atclient_esp32_static_components/components/atchops/CMakeLists.txt create mode 100644 examples/atclient_esp32_static_components/components/atchops/include/atchops/aes_ctr.h create mode 100644 examples/atclient_esp32_static_components/components/atchops/include/atchops/base64.h create mode 100644 examples/atclient_esp32_static_components/components/atchops/include/atchops/rsa.h create mode 100644 examples/atclient_esp32_static_components/components/atchops/include/atchops/sha.h rename examples/{atclient_esp32 => atclient_esp32_static_components/components/atchops}/lib/libatchops.a (100%) create mode 100644 examples/atclient_esp32_static_components/components/atclient/CMakeLists.txt create mode 100644 examples/atclient_esp32_static_components/components/atclient/include/atclient/at_logger.h create mode 100644 examples/atclient_esp32_static_components/components/atclient/include/atclient/atkeys_filereader.h create mode 100644 examples/atclient_esp32_static_components/components/atclient/include/atclient/connection.h rename examples/{atclient_esp32 => atclient_esp32_static_components/components/atclient}/lib/libatclient.a (95%) rename examples/{atclient_esp32 => atclient_esp32_static_components}/main/CMakeLists.txt (58%) create mode 100644 examples/atclient_esp32_static_components/main/main.c create mode 100644 examples/atclient_esp32_static_no_components/CMakeLists.txt create mode 100644 examples/atclient_esp32_static_no_components/main/CMakeLists.txt create mode 100644 examples/atclient_esp32_static_no_components/main/atchops/CMakeLists.txt create mode 100644 examples/atclient_esp32_static_no_components/main/atchops/include/atchops/aes_ctr.h create mode 100644 examples/atclient_esp32_static_no_components/main/atchops/include/atchops/base64.h create mode 100644 examples/atclient_esp32_static_no_components/main/atchops/include/atchops/rsa.h create mode 100644 examples/atclient_esp32_static_no_components/main/atchops/include/atchops/sha.h create mode 100644 examples/atclient_esp32_static_no_components/main/atchops/lib/libatchops.a create mode 100644 examples/atclient_esp32_static_no_components/main/atclient/CMakeLists.txt create mode 100644 examples/atclient_esp32_static_no_components/main/atclient/include/atclient/at_logger.h create mode 100644 examples/atclient_esp32_static_no_components/main/atclient/include/atclient/atkeys_filereader.h create mode 100644 examples/atclient_esp32_static_no_components/main/atclient/include/atclient/connection.h create mode 100644 examples/atclient_esp32_static_no_components/main/atclient/lib/libatclient.a create mode 100644 examples/atclient_esp32_static_no_components/main/main.c diff --git a/README.md b/README.md index 6c13db72..ac25df45 100644 --- a/README.md +++ b/README.md @@ -8,124 +8,19 @@ ## Packages -- `atchops` stands for cryptographic and hashing operations catered for the atProtocol, uses [MbedTLS crypto](https://github.com/Mbed-TLS/mbedtls) as a dependency. -- `atclient` is the core dependency for anything Atsign technology related. atclient depends on [atchops](./packages/atchops/README.md) and [MbedTLS](https://github.com/Mbed-TLS/mbedtls) -- `repl` is a demo application using atclient +- [atchops](./packages/atchops/README.md) stands for cryptographic and hashing operations catered for the atProtocol, uses [MbedTLS crypto](https://github.com/Mbed-TLS/mbedtls) as a dependency. +- [atclient](./packages/atclient/README.md) is the core dependency for anything Atsign technology related. atclient depends on [atchops](./packages/atchops/README.md) and [MbedTLS](https://github.com/Mbed-TLS/mbedtls) +- [atclient_espidf](./packages/atclient_espidf/README.md) -## Building Source +## Examples -To build the source code you will need to have [CMake](https://cmake.org/) installed. - -Most of the following steps will work with `atchops` and `atclient`: - -- [Installing on Linux/MacOS](#installing-on-linuxmacos) -- [Running Tests on Linux/MacOS](#running-tests-on-linuxmacos) -- [Installing on Windows](#installing-on-windows) - -### Installing on Linux/MacOS - -1. Get ahold of the source code either via git clone or from downloading the source from our releases: - -```sh -git clone https://github.com/atsign-foundation/at_c.git -cd at_c/packages/atclient -``` - -2. CMake configure - -```sh -cmake -S . -B build -``` - -Alternatively, if you have installed MbedTLS and/or AtChops from source already, you can avoid fetching it everytime with `ATCLIENT_FETCH_MBEDTLS=OFF` and `ATCLIENT_FETCH_ATCHOPS=OFF` respectively. Doing this drastically reduces the time it takes to configure the project: - -```sh -cmake -S . -B build -DATCLIENT_FETCH_MBEDTLS=OFF -DATCLIENT_FETCH_ATCHOPS=OFF -``` - -3. Install - -```sh -cmake --build build --target install -``` - -4. Building the source code will allow you to use the `atclient` library in your own CMake projects: - -```cmake -find_package(atclient REQUIRED CONFIG) -target_link_libraries(myproj PRIVATE atclient::atclient) -``` - -### Running Tests on Linux/MacOS - -1. Get ahold of the source code either via git clone or from downloading the source from our releases: - -```sh -git clone https://github.com/atsign-foundation/at_c.git -cd at_c/packages/atclient -``` - -2. CMake configure with `-DATCLIENT_BUILD_TESTS=ON` - -```sh -cmake -S . -B build -DATCLIENT_BUILD_TESTS=ON -``` - -3. Build (target is all by default) - -```sh -cmake --build build -``` - -4. Run tests - -```sh -cd build/tests && ctest -V --output-on-failure --timeout 10 -``` - -`--timeout 10` times out tests after 10 seconds - -### Installing on Windows - -Coming Soon! +- [repl](./examples/repl/README.md) is a command line interface for interacting with the atProtocol. Works on Desktop Linux/MacOS. ## Contributing Read [CONTRIBUTING.md](./CONTRIBUTING.md) for information on how to properly fork and open a pull request. -When creating - -- [Creating Tests](#creating-tests) -- [Adding New Source Files](#adding-new-source-files) -- [Adding New Include Headers](#adding-new-include-headers) - -### Creating Tests - -If you want to add a test in atclient, simply add a `test_*.c` file in the `tests` directory. CMake will automatically detect it and add it to the test suite. Ensure that the test file is named `test_*.c` or else it will not be detected. - -Ensure the file has a `int main(int argc, char **argv)` function and returns 0 on success and not 0 on failure. - -### Adding New Source Files - -This one is a little more tricky. Adding a new source file to the project requires a few steps: - -Add the source file to the `CMakeLists.txt` file in the `src` directory. This is so that CMake knows to compile the file. - -Example: - -```cmake -target_sources(atclient PRIVATE - ... - ${CMAKE_CURRENT_LIST_DIR}/src/folder/new_file.c - ... -) -``` - -### Adding New Include Headers - -Simply add the header inside of the `include/` directory. CMake will automatically detect it and add it to the include path. - -If it is added in a subdirectory (like `include/atclient/`), then the include path will be `atclient/` (e.g. `#include `) +When creating source files, include headers, or tests to certain packages, please follow the documentation in their according README files. ## Maintainers diff --git a/examples/atclient_esp32/CMakeLists.txt b/examples/atclient_esp32/CMakeLists.txt deleted file mode 100644 index e98bb22e..00000000 --- a/examples/atclient_esp32/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -set(EXTRA_COMPONENT_DIRS - ${CMAKE_SOURCE_DIR}/../../packages/atclient - ${CMAKE_SOURCE_DIR}/../../packages/atchops -) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) - -project(atclient_esp32) \ No newline at end of file diff --git a/examples/atclient_esp32/main/main.c b/examples/atclient_esp32/main/main.c deleted file mode 100644 index 7caafa31..00000000 --- a/examples/atclient_esp32/main/main.c +++ /dev/null @@ -1,74 +0,0 @@ - -#include -#include -#include -#include -#include "atclient/connection.h" - -#define ATSIGN "@jeremy_0" - -static void *without_at_symbol(const char *atsign, char *buf) -{ - int i = 0; - while (atsign[i] != '\0') - { - buf[i] = atsign[i + 1]; - i++; - } - buf[i] = '\0'; - return buf; -} - -void app_main(void) -{ - esp_wifi_set_mode(WIFI_MODE_STA); - - // 1. initialize buffer to use throughout program - size_t recvlen = 32768; - unsigned char *recv = malloc(sizeof(unsigned char) * recvlen); - size_t olen = 0; - - // 2. connect to root and find secondary address - - // 2a. establish connection to root - atclient_connection_ctx root_connection; - atclient_connection_init(&root_connection); - atclient_connection_connect(&root_connection, "root.atsign.org", 64); - printf("Connected to root\n"); - - // 2b. send atsign without @ symbol to root - - char *atsign_without_at = malloc(sizeof(char) * 100); - memset(atsign_without_at, 0, 100); - without_at_symbol(ATSIGN, atsign_without_at); - strcat(atsign_without_at, "\r\n"); - size_t atsign_without_atlen = strlen(atsign_without_at); - - printf("Sending to root: \"%s\"\n", atsign_without_at); - atclient_connection_send(&root_connection, recv, recvlen, &olen, (unsigned char *)atsign_without_at, atsign_without_atlen); - printf("Received from root: \"%.*s\"\n", (int)olen, recv); - - // 2c. parse secondary address - const size_t secondary_len = 100; - char *secondary_host = malloc(sizeof(char) * secondary_len); - char *secondary_port = malloc(sizeof(char) * secondary_len); - - int i = 0, c; - while ((c = recv[i]) != ':' && i < olen) - { - secondary_host[i] = c; - i++; - } - secondary_host[i] = '\0'; - i++; - int j = 0; - while ((c = recv[i]) != '\0' && i < olen) - { - secondary_port[j] = c; - i++; - j++; - } - - printf("secondary_host: %s\n", secondary_host); - printf("secondary_port: %s\n", secondary_port); -} \ No newline at end of file diff --git a/examples/atclient_esp32_source/CMakeLists.txt b/examples/atclient_esp32_source/CMakeLists.txt new file mode 100644 index 00000000..33043421 --- /dev/null +++ b/examples/atclient_esp32_source/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.19) + +set(EXTRA_COMPONENT_DIRS + ${CMAKE_SOURCE_DIR}/../../packages/atclient # match this to be the path to the root CMakeLists.txt of atclient package + ${CMAKE_SOURCE_DIR}/../../packages/atchops # match this to be the path to the root CMakeLists.txt of atchops package +) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(atclient_esp32_source) \ No newline at end of file diff --git a/examples/atclient_esp32_source/README.md b/examples/atclient_esp32_source/README.md new file mode 100644 index 00000000..69f5c2bb --- /dev/null +++ b/examples/atclient_esp32_source/README.md @@ -0,0 +1,37 @@ +# atclient_esp32_source + +This example shows you how to use atclient/atchops in your own ESP-IDF project by providing the path to the source code. + +## How to Consume Via Source Code + +In `make/CMakeLists.txt`, be sure to add the atclient and atchops components to the REQUIRES list: + +```cmake +idf_component_register( + SRCS "main.c" + REQUIRES mbedtls atclient atchops +) +``` + +In `./CMakeLists.txt`, add the path to the atclient and atchops source code via the EXTRA_COMPONENT_DIRS variable: + +```cmake +set(EXTRA_COMPONENT_DIRS + ${CMAKE_SOURCE_DIR}/../../packages/atclient # match this to be the path to the root CMakeLists.txt of atclient package + ${CMAKE_SOURCE_DIR}/../../packages/atchops # match this to be the path to the root CMakeLists.txt of atchops package +) +``` + +## Running the Example + +To run the example, you will need the ESP-IDF toolchain installed. See [ESP-IDF's Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) for more information. Ensure that your ESP32 is plugged into your computer with a micro USB data cable. + +Running the example via `get_idf && idf.py build && idf.py flash monitor` will give you something similar to: + +```sh +atchops_base64_encode: 0 +src: Lemonade! +dst: TGVtb25hZGUh +dst bytes: +54 47 56 74 62 32 35 68 5a 47 55 68 +``` \ No newline at end of file diff --git a/examples/atclient_esp32/include/atchops/aes_ctr.h b/examples/atclient_esp32_source/include/atchops/aes_ctr.h similarity index 100% rename from examples/atclient_esp32/include/atchops/aes_ctr.h rename to examples/atclient_esp32_source/include/atchops/aes_ctr.h diff --git a/examples/atclient_esp32/include/atchops/base64.h b/examples/atclient_esp32_source/include/atchops/base64.h similarity index 100% rename from examples/atclient_esp32/include/atchops/base64.h rename to examples/atclient_esp32_source/include/atchops/base64.h diff --git a/examples/atclient_esp32/include/atchops/rsa.h b/examples/atclient_esp32_source/include/atchops/rsa.h similarity index 100% rename from examples/atclient_esp32/include/atchops/rsa.h rename to examples/atclient_esp32_source/include/atchops/rsa.h diff --git a/examples/atclient_esp32/include/atchops/sha.h b/examples/atclient_esp32_source/include/atchops/sha.h similarity index 100% rename from examples/atclient_esp32/include/atchops/sha.h rename to examples/atclient_esp32_source/include/atchops/sha.h diff --git a/examples/atclient_esp32/include/atclient/at_logger.h b/examples/atclient_esp32_source/include/atclient/at_logger.h similarity index 100% rename from examples/atclient_esp32/include/atclient/at_logger.h rename to examples/atclient_esp32_source/include/atclient/at_logger.h diff --git a/examples/atclient_esp32/include/atclient/atkeys_filereader.h b/examples/atclient_esp32_source/include/atclient/atkeys_filereader.h similarity index 100% rename from examples/atclient_esp32/include/atclient/atkeys_filereader.h rename to examples/atclient_esp32_source/include/atclient/atkeys_filereader.h diff --git a/examples/atclient_esp32/include/atclient/connection.h b/examples/atclient_esp32_source/include/atclient/connection.h similarity index 100% rename from examples/atclient_esp32/include/atclient/connection.h rename to examples/atclient_esp32_source/include/atclient/connection.h diff --git a/examples/atclient_esp32_source/lib/libatchops.a b/examples/atclient_esp32_source/lib/libatchops.a new file mode 100644 index 0000000000000000000000000000000000000000..d7f89a82b1f77ad7897a9497ad2849684acfc06c GIT binary patch literal 74694 zcmeFa2b@*a)%SnSEmQ8D1~c?_XD9+P!!V49iaH=j5fN!3qQf*`aGFyn#wZ$M)M#{) zSc3_oF&Z&3YSdT(%cDkOi5m5Z8f(;OqDd^VzQ5nzYoELB9Z-DU=gI%`{@-_h=G^n$ zYp=a_->1wuXBSVYX|G#-NNju})s-u+W&N8-RP0|V8Y^O+=V9}g*lpg!p2w|WZO`rV zGio~PR=2iwRM#|gRM&O3S2wiOwQp$a^wQ<^4XN_lnvRA^l~ix7ZwRzS3S}*A@2IJ6 zYi;Xls_AT~ZtJRTYOFi4VM7opHMqTTE!5J(I~rHDq(n&1Cyk`%v14^j_3E0A)t)D% zE2}GOtzCnv-BWE&sM+xX*v}+hnoZlABy7@4UVBaN1vR~Diw9RNoI3xfiPJsrPg&m9nu3>i6g(f@6}u~( z*eb#;w5O}rish)Rz#A+QJ+ZD-)Th7U7e5vuh>ocwI$k?vTyD}^v;C~i+ zC-Ovbubn%l+!YOM-W9vTW!Kr;T3leGjDBg*wAn9>J#1RUTT=AJn3rDOG5K-N+ZDdC zc<9#RAy%g}rY(f6Ft9%4U37%@hUV6_4fTn(n)>?2mQ{(ymV^wDva&LSoBHjwpr)Xz zW2(z}Gjbl3DSGTwR&?gBjJv|uhinqXD8C@eAG#}Q#m64~@{XeCL(Jpgt;M~pP~~vz zj{a7Ls$FqQBJk>}T~L$1b&FKNH)Dfyob9c}Lv7q6V>^q7I_Yfxv+z5aPZSrRUT-@( zFql#==S7dt8g$mOAAUdETXMh`u8h}9%fuVj)YoQsEAHR^@S>cI+8N%0;$aU(?}`L< zd_~YgLH$Z8O#SAd4!?j4d;Te2j_(zz!FRClc@fMBUS8WC<@?2P0fQ!WTJ~Az9J>6lG;@ z!!8<$Hnwznkq(ALGVg^b+zt*u-S0G`99a!fMkLc3_p|2E&J_sCj9g=We}mtw@YDE> z^e-HTUH0wxiIqghW`7EZ`zTR#-&_j4_);c0uI~e2KZi4Vv^1AmAp;BfzZw@2{laRv3f`=gA-fK91F&Br8R%Unv!Xs4b z9mh}BH!_;BE94Jsuo{Xk!cQzW%JN^%F!QqlzQP~zhc#AsrPV(aoN4N?_s78O+Kf9G z<5IBKX9O~lH8o@my-Tr4=}ZI_8n3ZteoP7^7-M+RdHBitwTamm*|#x`7E9^%ZhA5noI39_ylL10rO%Wis7+sA-bzHxBXr6HR;scn2dDfAhN;p4UH)+VPuuF&&U` z{jbE%ZyvA$yD`{CV(Wi6^;-HaLCksZ&%rjJajoaK4rE?f!d9wHpyf6b^&ps*S8Hkm1z!ePbDq>@Yx^o7#H*|Xb zx@@NTI=1Jq4gLpXoNPJ;n}FFE#9r)w!x%jg{zhy=4q-9AY4pO~TTUB#1LK`-`~Yw8 zC@A^om^d4Or!vlaO#WPnbJc0XaDw9bKOVqN8t%U4v>|uX##JUf)IE3bMtXm0Ok4<+ zA7C3ikI7zRRDKTsHI%(Xv)3BE?eO1C*(A!YGkVX$|9i?h@#8;K$SEX>;4roX=P!1I zD`B_TUlv*iSpu?tn5TA%Y%M~L^UI6<GQ0#Em~n<;z_7ME@hfq{-BfrA3Y>8kS_SqB z=B#@z${g;Vj|w=!Bvj362%Y4aJim-c%yXNK6b-Y5X_%Gy5%^kI+$2e|fj#&syF_HT zMo1LHUxKXNTuVIOZLtEkbknv9t+; zbzmoqxS23Yko%$7?1YhH#_LS@52MUX7`bM`Xo8<5H4{djnFzY!J&$T;!pJvKZ-)N{ z%FJv~VDz4a|0!$(nRmaJ(c^?+=9J!M@NmK~(@CL(W(|CS%0;OYMn5xQWT9Li!JeHk z2AYx93RxpIJ7EkmMt=tX71->AG1%yFXxLFZ+UWcq;g4gpqkBxwnMm~w_+O`th4J^z zKN&Kdb$Fjp_64&wZUB!~V=xLwVzZOUp)#3VB@@!&COs(2gf!EncQo{m!e%F=*+wVS zJCj>v+%G88l`|R;%aLbHv0JPbL z{jIhet~?gd@HjEj<$Hx+SQ zzVH&PJkAB@ST#FuoDCu8f^A75BHA3=$>2h0Fst`kjcC|T23sIzR{vndW{hlujJ^5; zk(oKo&Y7H6ZkH%ATsFCB_5q19R%CWEuu~C7>GKHZT+m>{nQ4wlJ5S1tz?ty`GmTVp zO5}`yVVHOWQJL{mY*afVcnMx0aNJ)Ir06C7C9A{b>}IRUw8B8u8!(R)l2n8Ur{Jl5WiSh?PC&b7G~i(s@5 zx*fcJFMniSVLY5SC~tAUUVcG89KuI<@%SMS;qcPe3+EjeKPY}ce0CvDKt_4oMMVL9 zH6tIoI344TL0O}aLiT<}Emr8q<2JI7pm?sbH5rj|AjopYDaiHx*@aZiD~5sbkmN`F zaNZ$t+D;lO7!5;%5!%ZD4g|f+{6h!0jP5gF$^aTEjG9~&rH0Kew4r?harA)rJ_DS% zF9%}3APnVZSTDbKMpgfdry0BbqpBRCez#6Tl?{L_D2m7irs%=$pvs;~gCPqlSRxKd zks;zxEf|c$!mt!e&`6wphL4t9eRfvqU4Zp-S zJag2ucN_{;v=Db^5|E)wZ^*s;z11 zsu@4Cv2#{e?f9C`>bmi5HFYP}tZL|h&)l&e-_c&@?J;IqLq}VCLq|vB%JH48txX-{ z*LOCwbkvkWJh7s*p=srKQ0atWcD;iRBgLlY1)lh}E&7qqMQ5uBofOVSIB< z-Rj1chX3(QG&RJ6$Qp^G?7XoAqlL+Ov@_?NzRErI+p5%2#B?mCe}`|DNsr8+xqzKM=zwyK5)w@cx{s6}!4KQef5@&>4`X<`V@Z0Ny% zG~mfJ9stHV;-cSzaV)q0F|i6Ao$a_8Z{@V#YH>%jIf`bP&Y)#|XyQfqzy;%2k&GL=r#wENqp%`xSe=-Qd>IaIZ+Zf6rC**9%FqkEg$V|!*~ z<2O1!*^6ndFj@BzQ>s#z@5u;3AGCdj5R4^LB1lkPmIzr7(y-$xwa>IQ)nH0%Sl`*y z(Bf5Bn^~u^rMkJHxvp))o+oF0$Y@w!)7;i%Pehs;aUiS4+&HnKx}&wLy{=(AP1buI zt8q-jzZ^Tgj>eN4syn^rnx>}KI!uf04K>Zxwcr!ooLX)FW9&&LaEsken9O2bW83P6 zc9-Fny6V=ID^v50$X*u`nv6t}+SWoVO~4*^E880yJo#T}_1c=IAaxVagmbQh`C*=u zcD%;5jEt-?oj{=tlXxdH;!;K;)u_kSHSP85YT6sDW_>H3bufvV1iM=J6a)SvTL(4N zOOki{Z*XhFBpJkH=2AR+JX6puw!2eDo@bcDR-M<@)!E?%9mAVCV_|j0ev`OAqG7{A zG+#21_192us?`)CSy8salbS|H4w|T$MqR1N=C$14FonmEaC1_QBZKCw3Rf$p@{10B$(uEkMD_3^4)O9wtwv=`>m_4Gbtgq>; z(SDvd)OD7wZN*bGjZF=uwT+!C8ylMHsoT<8x~8kSt+cZS{Y?ZIXbq(u>l$&jP>RY~ ziz^JIz|mkUk|d3moCi!-aIF?RtYIs=Vltz+A%)~sr?HjIk>brf?63n9qwCsRJ34sf zQ95sC0tVABHO5FidAyKK*y|u~skug}p53yt)myx9M)lNb3y+$+c+rgNxw99|STJ?& z!h_3WQ>V?wuQ^HbR@AwmI z^|?P5+n)3hBrYXY@4YXaocl1(9+eC81X8&$&moly^Hfr~FwZrV3wv?pqkYzA(uFZg7tA^$iF5Yh57r^q-XGzaq{5tu+6oY5uFl zZ}XpH?tD2F+7B)$>VgN}(q|?)n7WC}4r4z4#;5M&*d#|&IdZu%ZDwNUk>jrOCdYf4 zVf$ECFx8p^rc-fQlShl}!Ne5Dm#&VBC!DYtk?Ea|SysnIupI?o&w?JCn&`X9`*3Rw?;(C7(qeh99`O*qojD&i z;0JCgHow@Qx122Lt{^Ay16PmD=Trix>xZM=)d@$kV^dc$HT0+}$v=0l-V$V8SE6I; zO6u0smE_ITmDHQ5E2%eASE4gT*{mXqO;cBrmZ>X|o4S&EGj%0)del_{#^9D?b8WhU zJOV#(rmhkO&5ZBrjd?EWW##7zvo0?bW?jB0%(~|IBb3c)@Pm$v?2KR%%NDhUjUBK*1-w}Bh_Rk8lFPZ)W9rhm{VHvkb*wq`m z1?B8NWLIzFMb73XyMA?`$P?I;ot?u)&i+Goc8(J{`w!XKSt)Y%AF`{HR*|#+kX^qz zRpgb}lU=_$N9623WM}6hk+c7hT|T#pyb61=%jb0>Xa6BP{o6#&{zG>9kBXfAhwS>* zA4SgoLw5OjhjKK~tJwdYj5=puBD;3~SmaBwCp-Bd_6O)Mhm7pTrs-FZuYlb2D^@<7 z=~s}~3p+ceUqQYaa^0^O=OCQ2MX+aIA+w)w<82hW{@DBsgJw=SNVpgx$82N9Wt(zy zaoLtW%1+1jJ4pBd#Wd&3X<`nU8G*yF37BVjPR?o0m(xb(!OzDJ9P{9G_%A2N2PvMZ znCDeahv!s|S=WwND`uZ`@-q~33_3aIcE?vKzC-a-ieFZoi}rPK2Pht?n9m+K9iE># zK1}fuikB-k&(ulX@_{@T_fEwhD()9b>Woo5TXCb}jf#J+c)Q}?lBEwk3U>YBIi>R& znH>S{O~rpzI{#Ag&lHEk$vo$h#ZE893B@B6gO$!KC7-MKXr;44 zF(0{db$+ViOURPX&5HRbmeb#+zYNgYm62^Tb2A_#cwM9T=6hWjxIlo6gQBeM|YBve_QdT zif>kYKRMIJ{jHL}sQ5jy^q)`3*o;FwH|NK}PNy$f`oloQ!<5cgB`;GvQRy6_D2D&rmwuinl2KrQ(OkQr=yPpHMo_EBQ-`-&6d);*ZHv zmKgf1<340bYk=aRN@t9cmnyDMItMHHRKe|u7~`6ryn3vsUD`u_m3$V(L;rFgC4?<&4t@!ez@Q}-$PGm1Y{9FHZ_ z-Bw$mOjJB_Z$ybypQ5C#TAM%1qSmB zsytwxTDkd$citS2QEbk&7zvK&cur@sVsow~It!HiSjERH?ohl=G0*><{WBEvJm1ML zQ+$QupDH%zW0IeHl>7n34=a9M@mq@juJ{ARpDE^JG%jyhig~^7gmt$49wz9Zt&YE`^Z@g~I=D!x>)IiHmL{6fjME51)LU-WS4KCk#S z#cwL+OG{4wpNjc%h?8e4=2QA^jEquTs(6B8KEdzwk5Ifo@e;*+y5H%yDPE`8JfAP~ z5+D6{`WGtRtayuJ^CZ2*y-msadXuwruVTJ3;N;IMepT@siup=_)6Y?CUV)Hw`z!e{ z#lsbkRa~ZcKg95JouD&3_> z*va{t+JRCJGZh~xOdCrS^V)&>l=FIl%;VKbig~?2`5?$QDn3*3`NI1^=OSSq(=He0 zJ=80OdEeq%#WyOxUGa9|DbRmN_)zep!qdRdD1J%tTf#G-^AE-U5}ptF7s9+(k%e;v zma!3BpcoI(8~KTl4--BKJW_F)@X3(xulNw*vmu`$d_MRHVczFhqf>R3w~bsU*NYCf1o%7+qC}$bn=9GZ0{!=0S^)86X2tSv%%vP?S6*Mxa)_!nW0g?|dq1%IYE1J^{%KhGcY74v$N@*2p82(JVW z7j6QNQCuP11^GdWd5uXsn;@Spd=7Yl@CD%G6xR#?0P<$V9l}3`oY$DNc@3D?m5#rw znAez;-vphDh3^LQdXsYUwTgM|Njd+Iahovv^Zkl>?MagKjbu9kc|Eu}EE%VpOIUbzc9OKac=-9vAaj;x5 z=X58ZMMipbi?BIfp_qNq$-T0U4b6B82K&x-yI<|7y`ib5tf{fHp}nT5EdAft_!qbN z>`mF1YPo-Mm+^r#uZ)lAcxAO69bQ>`D?NKM%iK&TtArZ!z{q>j+@mE)dM_*mnKX&j zH`I2ms$SV%)7&7*TS+4yvMIA%Q(N1PuLIfKSrMPr)Bc+Fnijl#kRscSsEO|bh*gx? zE5p<7&3Hu6E3*&Jm)T@Z&QJ+x4Y3GMTYGC8nsTpQ0-ETK!4ovRHeQX*xrhMIO~_T^ zuiVEo|Ht86%lHgqA3a?B(mlP+@Uw3@=j=^|K-*g}+VdvE5bfc#GqAS`tnxfbf@!RkIH1ah1guW3!vxX&_o7KK+shK z=faLmw|9trHh}i}U~~3Xrr9flJ`{;Q64+e5 zP(Ostxx@&(APE7?2JON8ax90P683GQ@VdM5yL5TD*LvHK?hY4-Fo*W3wBVC#iSk6! zS+>9Kn``^+FCSk^eDyciK4ONE3C{WK)x%AxjmMib)q5w5Z&7936|K4|ak|HEhLy&K zyt^kk=aM~r$BF$r!ouby%>V3d3bx&xyf^C7cC(1jbCjIzsDUZ279>W9GYmenG=aNF z=6J*jhcd5#R>XdjDa^}E2(u#m7)#jCxEWz#KdTJ8=y41QMMp9u676P4Boou27v%|e zI4kpD>@p%o&Ci+wwak3{gpQ2zqWB(cS!eNQB!7(ee&a4;^4)y-$rj z#>(cGYWzH-2}S!5!{$v8#`s>MpI6QlY_;H&_cEhB`xMChg*o*d_}EU~3L5tp#e12s z4JK@H{7mD!fR_9vxkXU95L=%4LQ0&MuZiS{|SElNLr?B zh9V|IW1zs|+6+ZfG89S4P$VTo5uKsm!oXK$Xty6JNc}QFIQh+jNb>6hQTx3D^N|96 zogg#)#eVxO0Q44yW~Yayy!Id4%`5)#^jG|IQk2l)bCd7i=c(87^Y@q(s>3;6)uB)ZyoU^wj3wnD@}Yk%)HrW{#BsfPs3qHGwoF#cKcG2J7-9Ff6BeHBXuIhPQGCl zRDgMPi|(u7qrm-nhC> zC=H%sP7=QjkD2HHor*cf4?1kx*p%rht?P<3O;eTGAD9%c;vlVc1i}5d49#sJ+DMn_e=z9>!2up;EJ(19!*BvaoBTnam&eaQC~?N zVg66YF-7j#Ri1ad=Sb-=O%{WJ&9B#V;y;hb%VD#3t!-@^a;3cXZ6r?wIG@E-wG~ z=h(EJ*qp57rk}{H$iC|I*;idzOkb5en7%3ul|bL*Q|#+}n!YFc$EtFfdjle`h5SH? z%jY;8^L_-GmxZS&K0}y!bcAA@2IC9%Xf1>y~mC6-C0e!lb&Ys z|JQfF6Ko%B(sbrN2>ZHojt41}{cdb%73&bcymLZHhr0_C7y0vpqD z!SCvY<6?LAc#lHco1*M-x^?#M0K2jS&Hopw2;!(mcMCRcZxQTmf-HfJ_3iBK z0&9EQ;OEyZopWIiLZIzE1A7l5FabHIeP{1ku!{rqcEZ+Q3F2JX+wePP5TBW!BRbr< zus?%!x?6DkUIAGG8;|)e-A}$$tjD0_CSIy7>TIFFen5 z<6|(wUA^!a>EbXRpE+JZ5a+^w`=@U_ynxd;Qh-s^{}W5t*noiH=75 zjkg7NnfRLfNaAZo_^+x<$M^zu_TTEzM*^oiUm*M%kPzMfT{|!{sm8m)t@v=QpObZV zuOnW19FL4VoG8C35iB%P8e8&ke6l~f*Vu_$w?s}i|6Q1p%e6?Z_pJ{aKhF-rj*s;S zeK>PzbhN*$q@>2nnPcPR9Gqt19BAO-*hfYVDjDMsj#;haV>zX%dY{H7_cTuK>vMIKEuQt;vOk}(E1F#RgQe=dl;vI= zl+BbZs>*#%fh*9#Hmf@D_PojA(f)xbPg1Feg*7=;DlgfH$_diRITEFg7F?0A*+W)7 z&USUOn}o(@O`f>4@YqPOVhrjfdfRXt*#wQaK5Bi}R;Gna4?-5Dh13Tjoym};6H&{X zf?EEeuI03=e0%Nh*0wo4bL$o<&cU&9p0-{K?kh zkAs3gT)1_|2*9g8!S9TseBdj>OoJZq1jRyyx?FMSg|(%PqVE zo9D!~ULidPOSD|!xuS51mT`GGNAA3G^uzZfj!tV%W^w9DGA>e1))_Ugy?kZoTdzGI zD!(^z;}u?znNq#x&8H>PieE!T%$%Gt=P*Q??(Ho8JSV=j_%mBOJBwetD;)TsTT%SQ z?6B$kJ2KmA-acbTqWzh0NzEG-HE&L?^#Je)K7Jp>xrM-A!ZSlt& zGE5z!|GLtCjeeM{pP#4H*2B4hHT0*qa#Y=Mm7$<-M5dYCPTP_A)2ka3PbAnuQpd;G zZ1iDt**P#8<9`-;C;Y^fozFexIaf;`@5H)9LTq47Rlj{yJ>;^Pbu>9 zK)yY5AKW61m^cx?7a)fxtYti#QVwelU5fLm@HcUwPOdrh9JJh;L+9dL6Kf7V4lS%X z#0MtBA0aq&)PM%q!OBACQ`vAMSuh|b69apYzMWkz@fl^J;izj((2mfbQ$yb=nZ0lSg%qGQQ3k^9J$qV?pb zaQYR!srL)u7{6|x6MeLRulU5fAc_8G_II)K;z!W)Le8bwMdByY^J+h8Wg$V{$O|)S z^mRW3Sx#nzSKEu?3KMOCS!Fzv<6B@qv(b2_$FHMjjq%KnKZ)FAo@hMF6Wrcs9oQSb1i<@thmqKu?G9ToS(m)sWd`if~n&%PM59H=b*}FpHXby8lB6 zBP?$06$o;&E-T>6HhG;jtGuiq*|O*H|4dn%ZP~GkR@UW3zd$atyfC9={lve3(fPh( zLDnw{K4kRTu9aTaFZ6wtn*USszFG7-CQFskJ z^W#m(Y4$`@p2y<6m6u&Pgy}r#6}$yK#^}XfBxroc}a0ydHI$yq$&Y5ey7F0}c&c_FX=3t3NgktoWb!yX-*kHQzlPAr&>V#m)yo?}fT z?_+rUG}>#9Gj1f_PESi8M#+lTAw1S1pf3v==R1jjZS3xA6L4fPv4eI%p2>X}?!12p9?^ujb5JKN{G zHTfm+vDvQy;t%s@-&_uLFFpcAh>z?0EZEP(hv~d{X)d)wUI7)DmNACK#UdC5@v`vE z5QTpT!#TwROjh1O=H}-9yx?1q#rvW_xz`nvy?8l2+f2(9#OsmM+*^#ND9$%#b8k1@ zvVVLZ6f5^m6EZmdD>PB=_5r-jnuxa|ce(e+w!t$!zLRktG!;C{Hti1}H@VBVjYqw- z#zVVNE6hyhKIP|vrMF@=1TH0%`#O4*m%A^}&uvB$q1-<+Lhb}KKqQyLAe#FqVrS&C zX))<+#x5@>%$GM}mB?ULezaf$6wFx5&n#>}xEKF53YDK_Mq9)SQ!hU|j8E@+vBd~3 z$d4D)Fiwk?pPSFvUS5NjpJx!GlREkN;e)AvAu=^OzgNMHqF?`JUKg=r+ef0%D$^tlKn%iI-;vJd_Pf6Y`44@i0(PTz= z>#|Qf4K!K<lT#I_{K*7$1j z`u`JEli8i|j0O-7>YqY&(+=!vcG17S4>Ek1 z311$BKM;iXc>o#6y3Cg33@Vr(Q;}!UOH-&L_JYg^zpB#b5Dr58V}>C&r^YZ{*~I93 zCQsD($gD^*tT-3fnT7@qbd&wBLY_;PvmZsQ^8m5c-=(<>Hl-r-=9X-YA5B}%bzeC-V z?Xr41d^1DlAr9SO+-&pwd;I|%m*MU|tsi0*-;0NQKI=HNm?qxz2hik7Z1va%nI{I{ z^39}lDg>us8@2;K{@cEpw!+=#o;(cCyLtZK{UPjy;qD7g9(D^-@Za}`vIB>^&pK^r zHS*)_ACUQ6dLIcF%cne*Ften*C|LmpBAM zcIE4nLbGlZ*GMwUQkk{d26JVmaCd*~s>9tQGR)dcW{IXqxVwnntZ;V$_c`J29PSJ3 z5^!^2o&#uS(!7^g24Ovff6&4;5VCLl#R^01I0mhStO)1(F=XAYwuHRdb*LD85rWz0 z4zR)D?$O8}*1Q_WyyKzDNn}M#u7vtWMV6*g9xa%5Z_V{quKe6M3 zpr~Lb6~fwA%nP4UrA@51!5Rp6m$Qhb3c}r$8D7aKC=7!_G7!bkAb1?s^MltwD^lTVs;|#-PnekVbS7uHo@~}Y_DJ&_y7hcVs_iH z;|*NQvD4!Jib`B>?l}1)o(`Ls`5i-Ckuw$p)jh*c>c7EggRS30X=RDhK zTOe42&5oV#+3wBf0=|!JST#q+`EKmIy}^#13*FdxcY_@}m%6d@#;HTAng6Xe$}Oo{m#v>h7P{6@@gmif&=b92~4)}7^Nc!@=@%A@7D5iDeB90s z(nl{bJ#n}dnqKog$ap+ILS$ymOSgI*7qmMe+`$oX74$eFszq-d3W?h-W<+!-d-p-c z5pi)K&UQQQhjAEO5zWXa}Q(D{ykPOuWZ8 z7L$(ui8(&O>+$}Czxkjd&l_nDFQ1gk(PdniGU9)7 zwjB=TBhTmX_~fczKq=H+InsP_;*)!A=;V=nQN;h`Pj;+K8D)+jpP9o<1U;Jk)IZE7eKrK&!DdJ50-N-YA>fB9>_}Z`HF@abS1RmLZn4#L z2O_rP)4gmvW+l7?I*I8i?~6<=Immf%5`uYJG@-Zc`YBVrofVHav*!sMM}Bp7x7Y5K z#I|FR!^CvN<*+^6#eEgWJvKXs?N5_JcAW12rsLiwnCDQvJ!#%cd>=v{_a4>4Yarwp ze8mbmdnay#j7OypMb-|PJ1PyrKZ$MUXAe(3Dm{aEtk`m^W5?iY5ONI8mxyL%M$9=F zXl4w4j&Po|Z<0veudZj9Juvx|JS=4;I0VroJS;JTdFWkC=#n5aXhF!B&aEO#Yr*{x z%z^rv&W0PP)O7n*_4nIFGXs3hr zO^o9&vSYT>j9Gs21dCsIW6a6lT60DhNsKjTee29gU#NTjzB^#c-(UhU5XPA?|Gv-i zjB049_xx`e%`;(V6Sh(;qU-r*7IBKW9DW{?OJB#2{{tiAr$#sdl>VKvD~#+3AaARb zeoomhjf|i4dkx#Djs_fg?ikEd`RoW!;i2~dkBK{tECK&uYzZ86O!KbmWBVEgOJXQw zY~G7R#?u!$7h`Y*f_W~sQ-V1P-8#xtJ>E>lSYdOKJ!G=?JY{wLN_)Kf9z^d;^zQkL zZX;&le5*!sl-1Ydh*3_0h*3(d2-WMN$hn_i;U&(LcvZ=G7fHO8dx{rgf}FIO#|_Dd zcS*!cdPF>)`QZR%jO!#ul|4)3b=G4&VyGaFdqayG=J$ec4 z=b-O#0-&r2GU_Fa%<7ehUZv9GWeN3WDZMG8w_NGX61_I1w?y=dA??%RP z@XafjL7RzrnM#+@f?YGc8`e1`xEg$IEk@YVpMsF8zEwc-_aw6B*ihg zQ{p`(O6Wj|-$BL>^`?=TPL=qh=zW&dD`8Wnc069il<~d??*tclWxU04AG#-w4ga&N z?1uBce4X8hpT2}{?r2#;7gux1g=@J6Z+_~cy#;Pb-d=7=-rjwDE{W?G;wU34>=t5? z-gq>*K<}Q`)=huu&Axrt4_(UZpBiRE1_VM`qc`P+yn*ua9l~Z8hG4|4uWJQ%VP3AU z%Y}IZr6I9Ez;`9x(z-ehSJ&m5yg{73T3t6KH#YGRLH>8Qj_}Czmj_3stm?3(G z3o!GeiKA0uS*0;4N*L;6Q!Jrs_SG7!t8r2du*+&FO$oPF%X+LA3`OP8$-OFUTs4*Z zT5Nb-k-K_>%Pzt0msa`+c28L3?n#5ocpxdqrdBKUL)8f5Q6hpDN)kVTM;vG8aYlKe z6ZxS>flj$VDRab*G0KaavfJkv@e=N)VcY%cu{qGj>apBWNv}~1uX3nMk24`_f%`Ss zP#o@F0tN2a+{h*W4Q#G!^}@!`(GjdJlF?6H<`4gRy@FTgocV_KPd?jCdJ$EURxm#Sfrk z{@F^tUTm5j#x`3@-TVULTaE5A?(Q@gbSd0Kf#4-045A^Ip%)>VW3s!DJD!@QWfBVJ zQDhWoG?1rdyI)Vb6d}kdcTVZcE1l-J+%@;(P2>gK*%n?l2iR(*+^sdZ>|E@+t(3dX zR(dmbOf#j@CqQtU5e6lO&nzu5&4`zAX9t6-sHG4#oAAZ>4csZ9P1w*KxI5dRigpzQ zj~QVQ%@5rI!5gW0H=!cAATz!{e&Cua?K!(k9FA4kn0 zIQ)07XWul6yX%`=!rSEt;ao~Qs6Qj`*+Y9nNB4p(Q~hr3Vw9dy5ZBiE*X^6OHnshD zO7F3`#c6m-tNGdKebcVB{MCKau4BD7J>$QShKxpALn@6ujZ!Z29E{SQH4*zS)r1*+ z;ohdt=w4{sXJ6MZhotvt*K$#O$2?^YL(8)MwJn?1y|k4wI{t64Wmhf#@=^PV|K+20 zkZUJ1D(!0Ldox-Grj6Dd_8~hHU{2$qX+@)F=lxD%qENK>y z8&*(oFanb!Oy>?b^Pd-QVOqy;dUT*yHBM)Z-No$oZ~i)d-BRN|3WW_ZzGY zZ|jWZG)KFyI2QaBkNqPCtJE`-yXigXH}=qB%Z~BPA$gutjb2S{&QWpMn^+|f=+?Hg zk*)!=T78O+orSfIJ+IJq?EGZfJ!nUpE`H?UvjL>J`<( z+CfP(P!s9Llu&k`v=Zx_4soq!>c;jsj>pupruR5ALZrvJ5gnA3HEyi2HDbzsh`k}r zaz99PEoQK)Y>$cdNw&G|%VK$NddxleN+EKDs5a@c(%4h8L#8sCSmZ*As!)N5-x8>*AvFT*ulbz4_$Q)3;U+jrq@0d;t-U7c8p zzNbmEe6@SAvbnz6WM=B3!)6^de_{2J)2kQEomN9?7s9TpOhuen*D;}*pHB<2 zLvdp>?E%Sqofh z8kLTV*si)xPu1MKqvp*BY_6{9SY6#%@6A{^zuFmxWnN`3JnHa8$4p%?1E1wv)l$>h zh0m;@?RTR%YwCjOM$t7FONEzUO_jDXQqb`24fTz6osF$6)$I)(jUAmeEp_;;9lqu^ zu>wuvDoZZwt5N?l!!XfVyC!<#66N1c1-Rg$A6RVj8-3L2b zyV{ZV9=;*yssbI&)<%#GXkS~?6m%myI^4?a$x)+=-?yWxe?zpsPVNXKTwU4=Hywcx*gVzbWmHwcdmBH@7ubvwA&bV-=IH zsjpBxQE{dF{Nn#1-%m_!qKf?{d1&71+6|rfc764<*48F0P%jgYZ5yxJRK=u9^t7Nl zO)sdfYwbcExo*cUmC}J)o7d%%%^xa5K}iwskdeCJvI-d6$_3Gb6LX?lBovlI$VL zo@Wf>*(~ZMM+*BBDzK$95w6Z_|{yro>3b+(kG`jb4+lPW%5gxIzLv&9i!OxL3^+% zOlNTVHMmz=y{e(J8qctG!E8fy>&l?iCXf%QI&GHL9B7mCm5dcvBr`H_{0t_eps+F# zIt6wkGqjv-*KoF3Ss$q*2z56>h9tXRaFFTQnUmeg*)|1nB@A-ZhJ%i|9~?+r9#~&) ze8@y?KhmH(sL7C&I~coKfn$EJ&Ik7DqpPkga?a(3x#@H%=OL~s+G^_SaX&gJoS7Wb zrz(4_>#F783#^zqC7252jKI!@=D6!pSgTHyT-BNG0c+?dn0DIh*VVK)@FstC8x{&_ z4ti41@Azh~v&U+;=XWIA16TXZQt z(TkL4b2%5BDpXIMw(zLAixbZZrQg?SuT_@KJaqt=2%fP(Z%sv0fUxJPQ5~hso{+EmYyaX;X+9LlOPd&G%zWX2X z?(`RlKKCP_OMO0GK^tWEf92i9tr9)Ar@s6D^X~Nd1P|@HJ@wuHskJ`D;t(Nlksjab z#dA{1_59Xut_sD=Z04(NNl`fED_#umoeTtp&DYVAqObq$-9gF7#@>)LKc8?;y0Di> zdO%@Q|4C8Uw2$_WN_vdGt}VvrQz}Up=D+Ne3-iBw%Hcv+`51qnH2>%{|CluYzJW#d z(B7zTokP4YBHa$Ip*a@CB}js!mb%aQeziS!X{utjK*6ZPOfBoFF*YcQKi&IU=VBk~a{v z7z=XQ%a#;j%Ehn?Njca%VUWv|1Our#BsyJF8_C-nF8`_LS~y&$Uo*H9Uu{RO*X=os z4oE#$c4;tacW&HM+l82CkbIfSIBWYdjpdL<&c5Xj!w*~$HYe{-7WrT$PbfK$LQbDE zj-S8}+&FAbUQRBiRG$5kbSJ~_6?5Gr#7B_#OCsB@+ommJ|%A^pCUK;6nTPi zMQ-vba+6Pyn|zAggsuH>_ne6EtuSMo(lzEsJVD>=~f znQ=H%KO-%hI+4RckM@#!GxZ>cgao4bEHs>{2RRg&dKhcvrXKdSZ0dmmI8zUDATae% zX62?Hr0i7?`I4@w2R`Hn*P|Xp$J7H8fiv~M2l?PkJ&2C32UB)t94-r+FZEDBmU5YT zkaG2>d?J3}Og%^$6Y#tCGW8&NF!dniHT59nWw0;hHT59nHT59*H1)thxGHSUzNrVX zZ|Xt%zb;D-_4lSMl7CYcX`_Tn%ald(W6C09+>}MiWy&JuGG&o+nX>4*Lq6C%&To*b8aGS5Xg-FX9h=8bk(H)I!gn#c>V zC&y*X&Y@i5&KI2`?8z?f3X!v4kX_u>luKMbeaduM$7C0GlgJa;lU>|%DVMlE6y_Dc zW-{!I5O#L9iJWC6#}oL0yBV9ab0-;b$6-%)aUT>p+mIZWzVx)nSx;oA|FX#0wq&Po z+KZI~mxZke%r+vsxU3_}DI>?F-I*uKbFe47wyUHZjm#^UDl&A~Mr3D`S4EWL&s-t7 zI$T1z#63=Qm?qi9JyGOr7qW}nLAk`;NQUtQ_GB0L0x;!l3$lxQ3FQ*^8qpbzJ=w+O zBi4+|vXWigdnlK<_lXYMh3w)!De_9}$*!&cNV&v)Lv$u%Pj+$tDe@`UlU>_|P`}Ji z6=Y>OL_79cLR?%S|$#E*f-H45Os1%ONyv;sFIm!f`q7 zn?5FK&7lsMcQ{O6f}N$%H+@O$uMs=!V`SIIR>5D?7e8>fVIxPde@7T*&C5HK=YR)_ zyg=CXfhv)+Pmx_8VEK!(Aw!yGPJ^BPkdfmP@dLL6o6}+bm=^mX+4YlprPCxj3GB(P zZqE|=2<*wO4lke_dAJDs9|$u)KPDrs(b$t+U%F1@jx!?MC)k|L2-^-iRgjUL&4kF=56P~)X1F)YqHCy$2LO#s~{uCClTzt z;quvAbf!a2c5&J6jLY^VyY^*#egQOseSvTh_S}5P>DV4_{TJ4|TSKQwm&x)cLf=Jo30+) zLBfrS+sKI9i9I(LcRd+xM0X0dgM?34%p$q8Jl>9r2FI0S);%gM&s`z(`dAxJ^`HSKY z74tmA=@cj)qIj%g^FF5Jd8v}~9-_0u`<{;1C~i}1UV;=mrzrUuiqBSjq2jHIf2sIR z#SbZdTJbB2-&OpX;#eq||9*-`D4wAB5XI&LW3GObyg_lh;*E;C72l`$F~x5w=Es&@ zJ?G#e$1$GO4tS*EeHB+Io~d||;u^&*ioc=wH;V69{E6Z$Okgf=eH51{9FfitkqZTg4wJ4&fTt<(VJAa6CvcUVjhd`zfBL_$bB874zd5&gNRh zH!0qw`1gvRRs5pj*A)Ln@q3CtRLr$$TpnVWG#!snT%q_7#kGoC6yK!y9>x5pi?d&@ z_(;X|ihrp1*NWd#9K$ik+2rxZ@sWxf6rZB_2a5lw_^*nG;JD;$9<2Cy#rz(+)A_aH z7Zty+n8z-6{EC9z@vDGxnKu%O`QKe9uTnf;@e0MKkn=FX!);XjV=^|nE6I}9FO|+s zitkhWpyFL*iTi@$zbW=`+;%!#m(1m-Kye?XQ>^486`Rk`Nm>U|jx^}_y>!RFP<*dq zu7l-t-c}sP34xOzsCb^@TE%B7zD)6N6#rFmes0qKK4i)NL^ApT-Tq*A&NEHvEKqy` zS=zBy@hYX$q2%inpQ3cmRlHg8ZA$-6#m|u?pD!x?zbKu*DdxYcT|Og<@pOGq_JNA` zRm?THT>Z=LUE_! zOB7$Pc$eZ&6zAjI)8*}8#f!<(e^!t&H__FD-T8K_(m7S}d5SkHzE1J&iXT$^3K?}l z_h-d#E1iESj^cdT>Ep@yfb+C9AowcuLGjUwzpeOE#kVPb zUGaxx953m@MM?W36i*=|A9ORxIA+rw0e07eOO#FxS=yyR@fxMmrQ{nFpQ>~&R`MSy z-lBBwQv7?ewF zR=k<)>Ql+DQ+xwi%5uNrw-tY(co=S`IQ?MzbO8@;tv$_|0B+Rf5p7d>Et66@2i;q zLvcF%-<;zE6;D+>L-Ab2{6C$GdyL}a6t^gDS8VQ0N&Re8@{1H-rkKyhxwL+&_!h;t zEB>A0Cls6eRFdwWm7E{JcIO^hiVGAMDITPFnBq~2$0|1WwdDL~KP8`{xJvO%#pd3Z z#O0^BT|QSRK1p$x;rfidQOLqxdAnT>Q%A z^GwC(DCUQhoz4}CxpbA2Z&S>VEIauQ#r)8+lXC?t$8RcrM=@7JaXKF<=7*S_JXdi- z@d(BHDmM2LrQW7dF2}FK6(6biXvO^GuG2qJal7KRiunOvr@vY8Rf@T4iPQO|;+quT zuGrk4bjMsJe@O9Tin-pIv;UupUsU|M;_Hs0y~{a zif1c6Qt=|iOBI{@qVUr-D>*+L?ChVRn9c0t{tF#0Ndq06@(1UHI6VpalKCH*^o<1N zGKY+yzVK+uW$qnExp4Zi-rc){N{}w9FkqTz$*&5tzTZ*I`|#A+7xGUP^PW8ArI5#k$AkGC8RZke0~HS! z=6TsT;RC_!tJEh?7M=#4Dm)83M|ci+p<>>{XWSzpZ&2JKycF_viZ=?chWu>B-xod+ z^2-!&5pIT@t6VXyR`9QcPXgaA+zH;U_#xr7kUt^J|B*hU_yyrjkaJNv+CK;UuJE~F zE_OjVIfOn%J|E2Q?mI3J?uLAT@DITy!WV(ZDxN5O3FK3RF9T0ge1!03$QKD;1?C!@ zOqaY;_-1gc;x6G^AU{>{nTpRBz70B;2;UCgqWD_josj=h_&)G$!oLIWQ2em)V~{_g z_&Hkbffl0+{z~Sl&0lF~xa`N01SRu2k_vVUFJe6i-t;NAW^o z&Pm4#b8OZM^BU+x#ht<>kbhJ0nZlKjpRf25;r${1vErWz^E!#^xv(q;gKtrMk8l;_ z4+%vRGe^dOC@Uf6{j%2!I{x6AK2QE}RShxf7k&4TNIo2jA zo+7*+^6A1SgZV!x+UIy&BFt;J<%;>gD(Y;6yhZpd@H)jCg*lGSR?J0P823kzU#57A z@a2$SBm5KauY|XPZx{Y4c)Q|0gTZ85!o2 z@WpGUb7b^Ux>ppxDa^T)|KFqz`6I=AF3HI=h4Y}p|8-J65IjI}i7@BY zeHB*RPk2f zvmw7u@y)_Ng#0eW{68%7!23@RD}GFva}gIgqz>nw=Y_8UzoMA`x24W)kiW0^b79UE znK@R6oGbh&xUb@2ibo1R2c0ruj{W@=R|#{R&Q^SsFrS%Ps`v!O^}_tbe6ujeb%$^P z_!MD|O|GWMGLpY1JPdrX;>(5kt@^8lr-6T|n5!$&&UDDPD}G3r&y_zRd?ffe#jgk- z1^Ju8i@<*q<{0@%xDEWd;!Ip$Fx|C~=L)X}_f-Y_&bU(5IzF(OBHVw=J~^QifE&0gZu>HUx8OCZWDe0@(qel6MhKtvlMqLzF3&&$y)qn2TK;Ak1Upp~6+*S&I3*0qsy;OUAK>?nK2W3G>*rPVqMtpDE1o z^*zO0yOK7s^R@_Afv;73qwryn-=X+E;dzkr*#_DnKP`MT_(jEU2rq~HJ;fgiS3}O5 zIJ85~P@E%N3wa-5jzz^IeY>qEbyjk%zWEKwYW^9hPD`uZ?@@EvkO2*iydl#GIPZhKM zoSfGN?*3jvG26q*IUhOp$~rbQch=Nm-`Q^Ws~zF%e>G)Ijri(aO;eeE_r9&?mlw+R zLOJ=xggtAUZz1eC*nK=;&l>LS|1y3?&nv61>8$a}YCAeS8->!nE6bN?>VmW~g%pb{ z0&8>J*Ri*5aOllE(1PA3a6<*&^+qJ5CB|gj4AZb}iBfl6_X1S)ew!L9(N-#gZ zWa~pz@KuUc_=ujC?MBqp*n(ti6co@a!}lhd`AMa+_2xrGWj0xpGgLFWj#z}J4XZY= z6Yr%x%B%UTLQ<1>tN!;#{5_L!Fy*qn3rI2UMp z_4DoZHtq4)>g@42qVx9{{420I=j`z~r0uOg+RnGM$LYt}y9})D@mhN>WX?Hz7eb)z zZCqgQm(U(=YX$c11Z#WzANKC--I``^?!u%!PCG7t&!*XXkj9mBUfzo#v##j&q{r(m zPKjTo_c!R!o@+PSdkcQ&T)j}H>!m*?@*AMbdci4NP>unRX?t9Sxt;=X&K~DcZSO%m zkZ}rhX|Dhq!`QD56?+w6y8Z}YpLC9TG|x1+cjc%=z{wPdqaIx~WaKJ(;HED{TuGOQ zNro}qjcN97Kxg9hpmWaNH`44~vkcd9kg*&*L^^vvNwfD)WR!i-IcM*(G<#c?;~hA$ z$3v^LcemJc@kgMs&vG8oqxUyy_BNhi&zG6M0iwt9K9**08|>}N8!O@vY}{|t?A?Zw z$PvA5dEHz=dw)!`_lFYCdjR$9oU`}0G`-75;(uivxZ<4NTL6~5O5}e$$=tkJ%vfUIqt04ywm2vX?oLU z^(+VV4u>A+Ip^A;H`#fxcllc?_FT}3Uglo_&Xx^)d#VD~EY^OT~Wz z_IRJgIfpCK>}`fU>d_vmGO$N|s0Ob89K7?2$*>HP!PuBC=NZnyZjJZg+~UWKw0NksVMuSyWcUPMb0JaICY&Y#8Tz zCj|9PL3si{$V4SQsO@2<=Nr#g{!FUz3a&_;j>#ko%jXK@Nu+-)M}3B_sAD{34(D^&&Z8G_RM(Q5F6;<4SNo ze~{dP*cD(^C@(rJoB833qWOgUi}^DSKVGlH@Do1Sk7C02UUQN2&tX`>Gw?@?P24C_ zw{fXx;}-CZ#f-L0n&iWGVi9YAdEo|YaSFvIPlimAnUPvV=ye0t7Fe}ootoF{L!=q* zWyL2uaX#xdpDB9<% zzKE!P<|x)Vyv7xphaWQ!V=g%#GO5CG@c5Z0oC4;(9!y1q>&!|4Ah)KSH?T^^{G zDrN1^D~62K&8OF_-avo(|LX2+L+rT9IDYS5ldiosiA{{%-Bj0hN$ZEWiH#6zln`HN zu$IuK7Gr#g`;ycqY0PejC786a78|f_qK0CtrC3T7QbDDlMv7oWtN4O~AR<9gq54Vc zhf+WI|DAcBng6}J+ooTHcwp}R%{k9JbIzGFXU@!>d;aaUts%N(&XK>>39X?`isQ&G zR$wf^>qo0av4+iTf`z>bZj6m!_b1cey-n)KKm-=&msXnI%X-tcEU!{uTSrSA8yWB_`tmw@KB4R7^GH_f{h=l^2vWbU zN3yz7pkCRdN-d+;_e4}iSM(}b58Z#PtvX*<1j>hx>wpYgabmN!9mxb8syWKms?-v$ z)~9q|`6^eLc?E&F$85K5;VX?AZCXW^uvMS!);7MQORBjO&Qr#cnTJk~J}`8X@Hx z4Q2Iae35q=3nuH=pik7BIweCATq@P7JxSLxpFhu|3#9qo z5q+qpoiADf$V^CJz+@X-yhARgHNma)G|WIwindOu$jF9=nR3!Qkvu!fDQAm~o^hI& zW_J3VC_WtdMltWdd|xEGE4S}MuW63r%v{}qO3b1bA>bybxB95!I=Rw4iOG{I>8Y%> z-FtUVe9T2zRE?4dIk-Qj`p0w6VbGzPK5*yWN;9hD7yW^HSA995H8kQT3t2S>(~fY6 z2`E2gucuDXA}Puoy=7kKIYrZ|w`I!VI&VTW#J4p9BeA0Yd&H1f1wIstar=G?}Z? znUG<%_7&AP!}&JNS%KD?zt_`B^7nem*p7q1X6+$;C=k^iE?AzMwnr)+C7wG5g)uET zH#Ij&{8!|tzG=0*J4JxIk)Nev;>j1x903Ge6HgxDZZs?IcJRsBN4vm=yu;mSYHNuP zo9n}dGQr&_7i}nQZU`H)1b3sn+ajJ#;BJ)tqY+P$;cnE!H1V zbOSugZUrpNyX~e$kjqNtWd@<6+}#jCmI=?Qbp%#-fS4-boz7Y{VN%J|*J+l>z!pD_Nq@l$ZL)o+Wlt^Uw#ehTZh zgzyXF^8ezfpFdjsdE>vrVZ&4K^z*#&3ueP_328HE%u~GdGh)2nY&ICb(`;@uzRhgP ze~P1AyNHi^*kksG%>Jv{9EI{8(*SMCAS?hzTSA7@vL!~>o4m0 zLE?i?8GprCSnrE&DECXBpUg-j`OU^_jMp1)Fuuw7X5$YTf5dpk_>l1t<737T8*}Z+ zd_G}(+V}^?rT>Xz^b?DJ*0}UDVe@B;|F`iA#{4vxd01pz`uP}8alJ?zt`~W(3F~-2 zF7M&Ih&+5i%zxqtA2i+(tY*~ugfTx_VNX22g~D8C4jJEV%=`n`TrHcE#*YNAR{Rsj zXM)!%{%PZ9g0E9N{RTc^`U-fn_z%W^34X8Q|7rZMV6Im$25%SBKi~(ZPk^VzD~(qL z?^gU(!LwrK|HK|N~-7EaV}#F`6T%GWK8SX z#yFPA8;x%;-e&xY-=D1SpD*ME#m4ym`E9>Z>Qf8-1Yy0FYYSB$^aq3yE$}8Sgd0~> z@31hwWP8#$#*C@m-wv&NAdRp|3#_J7!a{cMQ|h3Wm$TzM?ceX$is$Mh%kh4#dO3cq za`27_<)99-9DBr>S7GZvTIQ=4et36(gSs!Fz2c92r@wL?(Rzk4Ie(nQ2)QrLd-u0h z{_a);{l*>zVKO*SfdaEj#whr9Ah0#N8d#Fj2ih~5+jht@{jmVyXRDhWK7fU zd2v49Abwbx-vC6wj<8se%=*o;Q&H-WXzpASaXXWp_BCk|T)}N|i&O20(D)Fg$t7gC2ygtWCUhzillbu9J F{4Ylh@VNj0 literal 0 HcmV?d00001 diff --git a/examples/atclient_esp32_source/lib/libatclient.a b/examples/atclient_esp32_source/lib/libatclient.a new file mode 100644 index 0000000000000000000000000000000000000000..052923dd306883710e9116032d7e1f6c576cd0ea GIT binary patch literal 81640 zcmeFa3w&Kg*)P0S_S!qUJ1fb~&faZG(>A?d(lqxpEd_FKk|u4ECTS8%)7+Cbw`M0z zTC0?cSh*-5V#O-87AXiK2r4&4Ktx1EK|n!4<(12eh=7X73+MlzHM28G1A5NyJ>NOs z_xmPUYyQtO&ph+YGnX}M?R8nSv2CQI_e@`U%G8jdkq*}PDJfa&@}$HrpJ5o-%=?^e zVbC!2?dm4OnB3Y%`-ggZx<*9_G>2kd2{9rhg&j zoZ6r!S%P;u)6H%6{fzt-!(`US#v%GFwnT8_%oaDj2XJ=Oh+)Jh0blAQM1=)+6daG0 zjlHj)dSW6r(8uOcN;@{hET9eSL(8z)OPL&#NuIea-j9f-PAf6a+<|fo!%wSBD;9f$ zZE7AhQDLVZ)+$ktI;S4iI#G{p3ACH${sjgE`V7;Zz=SQ>*pBh++*1k!PnZO~Fv8pb z4}>7|V`PWl%mi~;Tw;tXZeCo&oCMQ92aU7Th>1B9EHub+qaqPE+5>DTZiFjcD z=ve#EU{8D7;8i<&v}-WjmIiiqR$5nocREm7X4cxQwHd8h8ClsGSsD4K3ibE3r}uPpoSNX| zOz#`)=pXCs`rmHHDb|PghtpKyICMsiPjqN_w6n9Tdu=b;mbh;S5Y4@@eXOs46uHh6 zt>i8=-0TQO$m7D?hN0{BxXf9Y-`zgRNQk6>5ru?Vm_=PmYJo#Ic}0qpTHz#|JIh9v z(?FOq!R>Mh{U%k+GEC0>rs>`w4RQI9Fx3>up{uw>2FN;kWVUT8Tl`GfVjykK@_j(& ze)O>vvE!j(cm(?iV|Z+|qql9u2=^W6Y8^EO(0$P3jM2W){w||^|7cg(5Lw0s!+kx2 zU7ab+`++_1Uv^&hYqhL!6rZYn)0T#m;>>j^PIpU58=y!>TYrBzC9S7(yltdAEeEKl zvppqkYfnlVn){5N!7=1W>+T*K6ictPu(Je1mhR5B(Kek9kB;TesxN+_+p zZ?wCwtG|=7gF|V%#|DPeM%&u^yCh>2%e%Djcpn^G+9$7wysCHE6W9ihkWU*cxfS-l37vl!yh9 z7VdU(cMgrALPl6fF>#8dUW*BI1<}x=62_+D;teS)J4S}WVa_ROTQ;T4Fo~>^jb+#v zTPfH$%{7VHr?q0Rd&p?mR@z!vv~6ouLw#v$RYiSiU18O>GctUIMHSdX7=d{iI5RUS znlbe;Fil~d&opZ#_iBcp)Wba77GPV9O-jcrLLVdbHo9GZ1LMl1bO_ z=^8H5aD|3zG`wBIJ2l*|;k_C@SHl-+_-YN`tl{qvOF!HLtbFoAP39NG@z^0eiA~{W zh^2pC*6`nnrA{xV0oBig#FC#$Ec0$8mhD&otlD(5CeuhPZD`SOt0pr*EcF~9X6Hn> zOq02aSoXIgz^cD}MU%Nz!{5{7ACU5#$$m+`wC7hE{;ekepTu&0cthhy;n1V%?$I#E zjPegnSD3{pOqVIVidfb+O~V`)il47xp5Cgu*J-##!=jH#|A?_54^o_GR9=o{%OPoS^+15)bFEL<5kEpLKOVn5972{4~QLfA@<`9WLr};C5K9us?iKWaj$ZU}M zZ_qH?mwdM4T^i=44#oeO#B9E2C8mupY51=abME972j<0;$a4;X>6?JWoT6t-On;;j zOPg~vyg_2xRxB}nP@&;!i8|}Y4oL31MnnFaAq8?6j)olWVr{^FsRHL32987G0&EORF~6~&uE56pOk=)WNh^H? zpg9&)Q2N-fb$#W~$1y;C9D7P%4X|z>`~6JzZI*co5Hsx3?BiUd^z{Ji`Z#vQ&`l2rQS+K$GQuADD8gT+RzYY$~X7?*T(E_i4?~oF`SePXO!Xo&c?M z0}45&{|%d7?oFVRv8kZS{RQ_9*DSe&O^u36L6r~KY z(#LhCuCEFDIEHB-+ga%wpQbMwgZ9%i_<3ZSzGKk013c>Extr2=fz+q!cy%<+d#34w zi1Ddua*yD6sq!FM)Pr(dH$g=tyaT!X2sK$>u9qmULKX%a+d&1Db^<{6%e&}wOCiL1 zEWxJg@vUk4DxvSBQifRRdy-RJHGhC{0%|KWe5zVO`PjW1Tn1AGvQxTv_yR6qE%VW%%KI1dc8p z85tT$85-^y?88q6yj@_C(bg5N+0!;qgX_qB9hJIX8YzHKbGZC1HO%G>=;?^sOqSTaPqnF`tfU` z%9@NdE5@(!Gx=tZ6tEtuGuHeNsSj;2y0fCn3XH!M`MU#A7v&n=i=)c&jiZapDvj=< zsIoJS?z$*Xp>a_eDm8EP(D;(dvg0L2vQ0~*x@S+DqC8+*p3{xwWL;vz6p6*Qbx{`d zC2#y-nJmbxqD+-8vwDi2BFLmbL@%oG9nTb*I>_LX?1XD#&b$jNH^KHpp$cLVOrK*`{z8pcN! z`oy@oKL-H;CkbG4ik}u~& z@_m?{B_EXU-Y7NGOm#{=bI8dFDY`$E?|Cx}yp6GWIP3UI)A7PdIVuk?R(?6T*hx7Y zh;Zb@aya6oLnD%zs-+P{!I7P{M?+G|M?H#@6z;Ogorf(_U50kFt<~2 zw2Rh{?3sJ3DYZ>-&`;c#kXO6c3g#CByJV^qhU@GRKS`<9nuy_H}S~5@E z;PYqTBrC`JEyVV-&|&2z5*zWYdkkxxFv_x$YpeGv z+VEqRRO2Id&epBkStJf6{Fw%A^RWiF{y#*yMnPGoG3#Sso`9IB*Fq!iURZLe)#Cjl zbqYV89U#ViPE@nQLl0T@mv!ohAqwm4?2ZF6}wtu8w!5i+HZQVMz-M1G~=VD)60V6%zVBX z^BO%9JRQnG1xa~>FFI)pNZG-K%y*&b6)ha(y4t$Pob_Me27^C9+16o4XHu|$k{3Hv za&RrFOU&7gUaS~Htpme`b(tC10cvS5k7=JaeFLCY22-f>3Mb#1Ag_p8S2@I` z1}4+IXNI;^Ap16jUZt3FGZmRHs==W>4fu< zr^NmW75pmUN<^FDTl=BlS)riZ<}WYSb0SZTeS*aEzRO@+y?r%_7i~JY$$ka4TQA3P zzT08f($H7LcZ-+z@ajZpOi(J_twOoRfxqL{P zOGc~}!hZJWp^LuI#L1c?#Y-qdOczbcAa^eqV(Mny#i)#O=;MmURQnifx70lCDB{E+ zB-qwU)B6%YP|O9Z%>)ugkk?tPRHt`a!ONI!jnmt_D2shC)}rFXJV+;5C8qauOn$*$ zHfE_AAYlZbBvs~6o*<4-hPBBIY(SD7e3L3G&83Z?{KhOYHzSiYJh2Up;kV8)y;oB0 z3$$jJ=}h=R52-d~+bzgxwL59v;BVMs9cGZ(Y}m*{MB>}9qsHnny@ePb!Qa!W-OlhZ zf&o%{Owr(0@Kt8-cPKmf94#Jjs9^Auqy`-&$-x9tLyi$kgZHuv4Lg!6gOAdry$+QT zyoqTe4wWC=&a|*Y6$HCUjXG4hF^k=%7B&hmL>s4lAIY&6Ik6VKGmjIIXmv2!1^X>z ziM4p-n#Var^^?m*;<;jf1Uaoes9AK8pKTWR1^C|U4fr2Jay)MU@+R7QP?Qmm8`?3k z#;{xCpf2P>u9Fap8$-sRcaA?5S266XpwXL@LTuOzkm8*y2+O`2ZR0&n5MKLEip>** zZTB)ISrC3$%(5*i_0D%a3!L0c1FcSIG*@^=p?Usu$YIRCAGVnDZ-8Xf{3DQY&3}iD zH2*zR!fh;g1~R^O&qc@}hNQ9ZBub6`o|}W@D7oCBz@sQ+F7d)ILRa)tLg0Q1tOq^k zIHeYjV;B9fNct6%I51O;g>S%;=qCjG&twag3qJ!3W~GW_Sv*cags`&{&RqBuYxJg< z!mF7s%HhsfNWHTbiGwq*J~@*KEuQ0KwE=y@(VSy;Febq3gPg8e(i<~1Z_Gc#A4JIt)r<~dMftfF@)=0*F6a;Y>T zR-nY#kQ7_vh((P}%4922cZOOy#~z=)VosMH^k zVz)?DGnsG#GBn`){Eo6Ps3%r}z>_e&Yvf{c=J<9z`b5fh4)36Dv!EJticFFXqw znouTW91~uV`F6{EGnw#?l(|Bg!0U(+6H1f`yyJrnamR<45QGd(_>pG9VwvwxA7Vn5 zlnJ6U3NQ5QCPW&dK#K9l5Qxp_g(_)6m5_0~z#B_w`moG5lNUN=zKf+ym@W_c?d6`?XTJNMgR1oqTQ==ZuJ7)i_A~EW|kDvO*(}SXzkjZDvL$1)fElN3Ts# zlS1ZdP5|tamt%7#fXq@c0c=|%CV*@)0hrFn&lMy835fmzn=|siYW@UDmvNn4#F%Dyw709yaL;bTR5W>iUZq?pih!_7-QcoNj?J= zc?$Hs*u>1j$>aHWRXfz)d9=h>#YutF^;22jKv>%NdY6%o1aj9};v?o-Q$|faix)(4;53 zUCwY4RmhPF#&wBF1;>%hqxB^+c?`F-u(NQU7bDX-8Z8R2y`7r_e~dbrM=7xEWx^P7 z_?$o|8O62=lb>klXB~}ZzOib)WW6p68HHFHc&wO96m+(kVXWeB_3R?EowAv;OtMxx ztf(+@^61K(XH4egah5r2CUepSWOd4%Ec2kuIX;>5LMe9fWX>xk>q{~x%RDY~-a47{ zMk)5-WX?M!>uH(O+i9f!Nao~3kYN~U=fOvuJz|VB-e(l&Kn`&(Sa1ma7Y`v#K7C|O zefr4Fr;l)&BDrqonWX4BW=xFpY!7c$^Tuiq7GW7>nib;lA>SfAnv{<0F^N;;ZCySe z>9Oh=r?^BsS){9uhiK#}B8vKjgnP2^gel6fBngC!6| z*&&n9RZ)13^Hf!m^K{nSb>bPT(-Kk7jnF-hf)q=}V<2&7#_$2HNsH#Mgjg~PSTOZ* zEa!Qxg;Sa8F|HIYToz4DoAPMY;^|LnEx`j>2A|ZzY(F($MdFm4%ckUXp8Hxp!_!$S zz>7S1CZE_^>5K3Z(5Y6bhrn3*)!NfxsVV_$EL}C-koJL3eet(gJoUv$T+W^HKo^)I zjG|oA*v3c+ZHqh_#wV`O_YEHZqKwRI8FN9(C%|NW5wQp!7*N_;L=*uONz-<#9Vtc+ zLdp_05&g``!$@3{o@OlOV!=0!hG`nbPwmA*ZOax!S2}Pb6!9ig(`dO45sLs+d2#47 z!EyFP`lf_ViP0*eG*%z;f>D9ZH0l|#WU?7w^x73M*#;%9f-SH_e?=ptg0tZvCKGCaWC)v!;P%?B^>!60%>^H!P6g9HPwTjF{eD} z=}uKojIP3iVtD4$d5D$Is;*Tj;;~jYI2nqw7P4MlcD72v$r5flM=??2;nmfOzY=^t zODc3xdzwm#Xirxh+`;$nXp{EN7XNgrlNP}A!goK^<1{f3w|{Ce#CS- z*ioW7jz5H--DrT1iMl2_iF8erP#bBQ5c1Qp{}cIH?ElI9tnSR^Pxglos6V{u5$>9+ z{LIOM{>2ZGBF>TV=`xP^eVytzv1m;c#6E(^9nq97bL7i7SH>HsYs&vun(*VNuw{dc zm2GEE*R){iCQjQJ6V51!S8c>Gz?TioM$E>y5z{zG?v*1OuN#n~&+U56dC7nnubdl1!6*xh zRd;00xyk&1Ne6}VK%-ARQDTHdrXFD+`V0!3EEfI58St~@V8@ijyGZU=wkxdVln&=Peu978q64UQn%+XcKedJ2 zLDeN9M{=hPLj{OE+-_itLzW@l$kp09I?#&OO~@4=@0QgH;ky>}7b;A?xuM&6p&0I&7=xMZ zZDCY`s(SJ22cR&Rc!d~i+}bwM6E<2qhKBa^Q7GKgKPJnddfWjsg5q1d``aki7jEsC zXoHXO7L;jkia=_6{~o8BT)sl*zEM&9PP>i#NB{8{_Z> z{PW-UIuNI!vy6_hkrAAY^zU!o*VlzodW^D)D%c|X75mcuF1*?WuLwbF4fKUOro0QJ zuyk8%>6YTU=9>D}nuemPisIJIrOl%2i#BQ_dOTGj^@$E2vL5Zx2VO z!x>S3Ul=s|J*uyb$tfk&RWugj-5)GsCMOHk|CWztd?^y%pK|JXT%^Ipt4!!Fj1JKs zMn?uaaNpQWhQOmE`?VLPh%vdpvkjf8H7r`c)p0Y13TGa6{nqdl7Prybx;GXhk)KyE^*tj+#!H3UNGe z$q8z_6HO~lN-Mlt^IB22d&+M;(XcZamu!~PMX$Gg*W+N!tWZzi0x|S zbir{rjOPkkF<#n^bYoPm5|^0*CSV<4Xxzds8;->^Y_R^V&KC`NWFPdzG0) zKCiSZKCiDckWZ}myu!{vJ~6I(30s{XSR6htMzfBr1FyZWl9+GIz`w}Li`B7`uP!uG zrwXdQQo*1u#{AufGHbEzL4N927pG^+y9?>e%a}ozaX8%5A{9=3uM<}DTA1m*O~!>R9a!kvIxaEYe@CmAkrJDN@}pCo`>_=zPXIULmi_?NA(f*&AP z5r|1|lr(!)0OPb_TKd`3=#FVLf2~u2`uZfno)*C01u79S(=VPzUonmT+%)=2lHP?l zfcCv@TDm;#n0f_!hQpupGGciGCJ(SPiHRetv$(@yT`ZHF_3TW-&Z0q{YRSX3b4Zm( zlbJG!^|ZL*P`&4hBTZqB2vc@D`b+VH8s=}|N+wz3r)YSoCbLrGbH-IVGc+0gw59m@ z8fO1cWdY(67x(;`Y7z_kC12Ptv9MoaVZX$}eu;(sa#*EMCm9H**e`t|>`xIQ;z*=y z6ZT8KuwTjx`z5~s*_3<wNr7JFbKw)oU#anhPh?)vrW8aFK9Rl^K9M#HpDc4^3J{qR3!g|_ zPX2O7M)*YXg-;|FK9N}XM9S~bbnen{r-plprF_4}AJ*`wh9@-ngBt&khA-6cVPdKO zQjLGP#=lDAAJO9DU&BESCuulY!zmhGs$uF0^4n?!PpsZeH%Ibm3$fzUbjs5%;utwDxS}JUx`~y1f#lO(V#TkKeA-T|^fXC6>p`sK zyCpvbF|p$BlYG{XSn1)7ShQy)VqzuFwj+NHVq(Q-8!Cp9mJuTtFz4-9?YQAlFzny zUE}{<@@Ym8Wh*_W5rfIL$k+Il8a`XY{Tlw5hCi)g-Wj3v-=|@&JXsd|8|y=yEOCsS zTWB})QkE+&%A_DBR(|HxLq6M`Sot}NeB@=jaBZS&*dQ^_DvF7bmwkg+<*gwfc^eUL zlbEY)(&Xg?@E3NVq%py3iV-Lo?Y?%Im}y*bYhj454e$E z1wOIL%Nu8zmuEa{r3~AUSmiw``Ssuv$1DOsxQl$`W#8n(OT_fm4~bD$lf*HLDeatT zsdshqtU|r3lhYqD`!f4e5T`6W8_SV=j!n_uAmayS9Vm|?#M+iP3HUt8Pey#F#3_jX zA~E~;o*DeA-VOLt%tOY~<4xVl}>aRzf?umQNvuOuodbzUw5PZA}~_ zkIQ9}Ux1ic@wZDp+m~4JJ0!mxF>%at><~t=v0VCtSjk@~`D{mG#TR`Z^7Y_}J}>Rz zdY*dNUc^fOO_IL@F|pEfkL2${Osx2iNPZ_`V#R+_@_P{zEB!A>em`PjCI6P>4?jx4|NtXPB;1etPHIjb_d}77lAo=tevEr9W{$cQmmHqXS ze<}FHN}lr??YSI$VkIx;eDJRVpIFIHNckh+6DxV%7Dzou!6#Po;urz`G4P3FRsbNd z-}2qI48$>VUE~K=@4O{e{L>_#vc!td;YxWMF|p#Wk$mbPR{R{v453pFYT<6@+pXkl|1_;?OBSLSn&bHdpIq`w2ANJqz&BEdlXr=>BbI$<()3^ zA`P!3Mqbv5o64I(j5&>g^%4)HB3`fI0%GJXLCj6%EhonOz)*!vy#uyZ!>kMQ?m)~< z<=sW>!w#Vn+vyVbY1jj7^70dcjm-=QD6VoTj4l%4&n2dl5LkEhj$hVY$#c!AaGr+G z(6FeZENipIuhVd|hI=&Jui*n4K2O7+((pAJ<~mK;cDIIqr{UK${H})i1eeOoHJifA zHJq*CS`DA0;oTY*?=h2pxLo6ZQNusc@UJ!eqK4nnuo*RJTbzdHX_(*NQTFF*xJbh_ z8s4d4zE4%@qv4$z?$Up4H)e5~XXG`vv5X&SE5aEpd}HM~#57i#$P z8opk`cWC$l4gXxj&uREi8vdt-akovRE^{=zOvBk4uG8=rHGG4Hf3D%*X!sQk|6Rit zj!_YRYIuQ$vow6BhHEt3sNo$NZr5lj%zX>*JM7W;m>LKn1*lA@NF8tTf+}(_$L~EQp0~FmSgrW#Hc63 zUxC$HLOdlWW%$UOde48nhJ(aXW}(JkqT!XAOrgdv)o`UI)1dL2HM~=k=^~ak@7C}~ zh|&KTKB4iyr14K^{5yy-{u%Dp_>XGxzt{M0YVx=jF=9iSh6^>^rQvxI zpoU-4@IQ!UUOWL9DK}fgn>4&f!v~3F-YYf!w>12ShF{b0`@}MD2q)Fb=j%0Gt>L{I zzC^>fX!rpQKS3

lKavPh#1x@V&RlI@t%T*2y9ArA!Jj@-Qsb@G4EFM8jJ(nQae6EHs(C}emY1?Ns{^vA&RFk=0<9|)V-_T_4*7)Dk@PowC&S!{a zd;L!1|5d|5tkaczwuUQ-W!?sjKdRwNG?}Y4{v8^ARFip1#A0w9jzn&QD28OQzt8=t(X)@m@mi^}k8h(UW=KYO^|Ds_FQBG<=DMuhsBv8vcQXf2HAdHYgUTCO2cUy=DDqs$=C1}4cBS7MZ-HaJfh)!8vdw;&(rV`V%e{bYWOP} zzFxz(YWNNf-=pFCH2j2ypVsi7HThKqEl9~Iqd7W43 zSp$`5sX zIjG?aH2et-^AK)gy~uz=ZX_?STdHGU68W;fC6h043i)zfy_9^3SCWr&1BNx&)VU04 zWt%u&$YZ5AUPzrZMU^G&kveIQDofZS_0t|zR-9HZej7mP`3Q8Vyz{vxmwD$xeuK1O zv4-(HrQjnn*p8GT#zR>G^Uz-^F>PdfQHFTC#Jt|ws^MOV`TNpdiTU&BehnXzcoF!Y zl$giHD>TgOODv1O;e176UIV^K!*@u`>)d<;C*_GBlDG``XA+kKKc(T{N?Zv(-&)4J zjlh4_@Y@pa0^dYKQl=f4*Qpdvl(-Z8c^Y0SaX!{C=nd=R)=!;KPO z1b(Z;hk<)FyjS9j!QZdp^Ci9#{7WRh3YhP|qy5C!NPIQ$S0p|T{51{VF7Zv^-z)Lg zfgjTF&m{gP_zf#X7zijFPt>KWw{7rS9#1`-(4X>7%Yu+pk^SDHv0q{#C z=6wjf?n*v!gT%bo;B1Nc7LYa#@0K_R{1FX*MB+T~d2N$&(uXt;Acw|D(kGz34TG`7)TlOWXj=A7W{9 z6R=mq@e=c%pd^X;TXBkpS4zAK{0xcPf%7Hq1TK`g8@N(po?Gx*G0P=xmYBae^M$YE z^PFS1hDRhG0snx+JQq1%V!pWM5{da6^;Hso1ek9{rB33nNc=J2nw)DquI(A++rXaGb=~0MFL& zLWy~9v{K^hfHO3lFY&G57fAeV;0lTF0^TYy&y5-+=5K&IHQb}&0S)h!_-BykHE{JU z#mhB(wZuHu@U^CtA-+!HIN+~q_}dcmUX}Ya{E)<47d#X<~4lEbKcI@@CJ!F zhZjhE0=P`#+kmSiz8|Uo`(Qv24zW{%?#Lof`OZ*D(K8fD}K38J?p8N?7^LGK- z$@{IYk~j!_jl_$A`5OUcvVi#;0Ws&OTQ&S0i8)_=SK=yQ{$4=&7T`xD=GcE+VvgBo zH2iysIeuS~nB(y^i7x}@_ob+lZy)YJkhF{R|>l%Jn!{XiyX*0)+vN=V=Yc!m%VIIqse2s>8Xt-Cy91lwV zkcN3&QG6aJ6h5KhZ)o@)4L_{mCp7%LhF{h2+ZwibZ3N?sL0q$vI9cP1`#mH-N8^j@ zPUxQuRpd+Dq~U-2Td^}e5+j~|p8kp0j{ZJ8jI2M2E}qT#*J937IsaNtJcu*H$7lIv zTk)CM4>47Jcy?wZXZ)V*%%b>a>j&!+HTgG{>9zhq>zyk9fB9p!Dd?l<_Tt$HMB-k$ z0s5J0xbL^c6{5KqkV4r{ms0D55?d zqm;fuVBNmQK%c;-g3{Lq262H*ID(0oW1ISTY*YHK2G;d)-r+Gz1*Pu_Fm!zv;(`(9 zXX@jWsPugYSl4%ygccO}UGRumH--=>)|eF<`W7E%ROZU{WR+~ESOb5SXl%iq9M zx$C7qRqj1VNL9+D9M$(oxk$N?<1>>gP#*)&!H5e;AY_yn#x7ZI05sEBZ*g2ftn~5v z!7k7$D1AKN(e+)94$k8x>k-7J^oe7T%8d90=o{8(rSEIg^c}(?fyZ3x<8ngjyB~7+ zkA!-3I-gv_kg3vmZ@+He!3xk=z)@ckHl^>U)Aa2;4c~D^+o_=R{cf7vtI6=Q>WmOp za?b+j zP+HVmUEJ7MJ+ZmIaImOnOJh;tP<=&d|5)dy#{KOBjTseXTl$-`8~3+0)#X-{RrYse zHTL>C2DS`Uly>fK&u$#Au4@=C9d9matgS669iOOd$gC`f;J)_k+Mey;l=pUQsoq^U zzO}wEqq?MUoSSdEU^akRnps`nESTGO7w)gF4UZSs3T2y0$158f4wP=ME~*ul7WYC|EdSqKuQD4ob6qg(O*>5 zJYH7VT(Nn)xu~eNp&VU%Q*m*46Pm89XuP_px~PKvptE$mv?zVNwxYVQxyT1i#f9k8 zQpr{*DJ?6i9Scu%Wp6KQuNvtutgPMI+&z+;RaI79wW+!&Unr>yamDV6P5ryG@>)j6s!MbWN{fn$o5w3l3j2zBdPa(RSWd?{I#ToY zw(`1+j*_8$RoOigRRep*+BY?hHD_0hwP#ljRvmB#R(V~1X+=q4S9Q_;o=tlTn|pg( zhC0jZ#(i7+^7nO>l#Vs;&keV*TleMe>locZNJ zBGJ9~(>Jw6zMj$idW>y{srC8dana}DpW?FpdveQb_SR(gHf$@~x218gt7>y|b6#d< zeO=ymUrqg%{*M0I(Vn)Fz2m9bITcmwyAM=#ju%#Sj%`hE>FVFIZcjyJ>cG03vHgXq zU45f<&G~ye@-o)d?zdEmBb82qGf!@veyNf%k#;P|p zSLJQqcc5ug-L{^-?EO_eySv6~M~iFrR5TvQE6J*@J>VglX29&F#ftz@)$-JagmqKW}uf8~~@ zp`y(GqUzGk6?=1POB%K|4b}B@caN3y^zQD;+&;2teRWl8d3#@YZ~FeSt((T1O4ir) zHRYG(g~#)@=lI(86^`vI2_G2i%B{){AIQ($oLX9()0{qD)>yJ-U&~NVeREUWP+m($ zcWQs_=8lZfd`zte_GE5tY8=ZS8P4+UudFW4YwNFSFY4Y}Qdrfpuep5P;JVJC%)*xb za8AYg&di#e-Gh5dMrwy9in1H_)~@eu?9Z+$-CVmad!j6Jt8f42+)ceDB~AH5joq6z zm*tM7c6N1Cgv)c9+v>;H4b)_BDx0X>ws&84YDRxUS>;e)ZdFHaXIo~@`r-8%6`f&U zZ*l3w$WT>g>bilE{S|wfYP!m*bL)Gyt{=%5+1JumRJL_rW7dI;p5g4=mW>w6nBQ=7Io59f{S z?#!*+o_V0XaeL{Wsx1?x>o!%^_m(znf}4Aa8aLIf8{W3PzM($6x5+nHRo6aVzIABt zy592fo-@wyi6dU=mXcGCg7|Jup<#PJ8FhZUiR0(_?NP^%SJ zVfXPbH#zu*_T$GVBpj)FM56O~>|NH0a|SFTpnrW-Md0{imy>$3NXB(CZkO?hjL&ys zd$$vx<9j%O!@Fe0{U+|=5s)XPRJE`1k%arNUqY5t1lBcv+x|}bNm+uOs?fvdQ%CtTLtXVvnx%M)&b!hn&*S@ZEpx(7iq-d-VaA&2OnInu#t&r1`FX#mxAM zv|D{P{Eq>su+2La)9i`X?4XKQC)xd&+R5w7u)W z`*wd*X}@~6d-o%k*|>e;ns43~V|0IE*5}=J)GD_J=UFj_zv%7B|H+g2?+w@w{pEdQ zUU#3n*S&km;wab!Isx=<-^Zf381iZKDpkbThd|0hn7XZxk{q4pN;9qFYEPv$($m23bME^3J zxH#)&%Z$TGwEcUWMcfGMcmk{fzP-)&jJlSAVR^8_*R_ifxiYbHThViHRl*$={W2cL zbbHf0kXW&$vC^Wev90$hZodw0@91iafRa59m6=ljrWfPB=$ zk9}LmXYqo(&AY{|6VddCVdL!1ux@a_!w4TpMakBUE@q3hEDv2WTUc(LfJ;4#=k7yF zoOKZU_+R5{)sE;nUj7he?}2D^lE5gMs;>uI6phb)FNRw8l6iboGd`Nlz!_L6zr~52HSoXWDYk?rV_G~|hLP6Me zGn(C9BnZF#8k)&nEQp|e1sv%v5k!*xClaNCNVcEAMHY9NAX1D37rKGhbZ_ea*m$YC#d`*n zJAOPnKy27G%(%lt4_Wp}^mz9|_F&}N#C&wYy)4xLVu@wxAM=njVN!@zOl zbKgu$3O(!wh0lGHN)ElpeE8h=&FCO0v-!DiH$M0MO;AfijqFbN+&6D*SQ+{ib>eg1 z%(o`=1om!x?weFbCQiX#^=6&hqxrf#&I7r6WJi;q03o1KKD(3SB1XA(($=(4{G~#=nQJX=e}9@7ea5d z4*1;npP=QX(9QH9KKIQMUWJoC1#R*CJ-XL@mp2bgKR);EzB?dV#E;K?yYCUTu>APk zxBFg!z4qO-<-4;kMnSgyUogS_Jwe3z`MGcReF?ju)^8WF=N%rHc_k#hpW3tUV zN!$vuy;Q;>v2RGetz!T z{kSM62Lx<>LfE_!1dRTrD73(Cr=d?L{0ezW>_(2%UnRT@Vw0br`*uGo6qMU7?1s;Y zJT*4o_Thfs_j?#pZ}awX_lq`Xo+kSWYI`}3Gu{sS$Lu$+i1FBI%wbE@r%9||66=*D zNb=!kei9vX`==4Ph~q5!TI9MIK+7danX3&+bIFLa`YHYzbkP@@I9W+joVTP2m2}ah z403-D4DtP7)?JKBeD0g$>mQ27OkBtY!sovEvF#uqE3@7B-1k}NL?JP!;&b05jL>J< z+wi$>7Hfr0gQOdu`xfI4Ww8&&SyUXn9z!z8jn94a1KXiwHYPszO~MHAd385F_f5(Z z`UR&-eD0eEIXiSC{fy6jKL84!`zG@^#H8WD=h3bFZhY?hWvV@cS$CPvgde&M9nX!= zeLJ>&gl&Y+ee+=84f$y)KKIRRHf$s}cr$8SaR19$;2VhbKjI)8A_y= z@wsnO8KF;e;>PE`N#%$Bz-0qI_f4uGREoVDpZg|-&waDo@Z;LT3;5jkYABDh$ceM) zoq3#yM5}{wr(p9d;Bgj@T=O`GsDARINPJT4{|pg}_r-dX5=Wph`Ax8`5j324Y48d# z@XZ7;7Q)HijNl0{taITN@5bP_iSNX8;;jokNzA*vW~~l9Ld>VHd_{it0?{LPL5Ua@ z#{6ij2of=$j=L7T1@q9B#)1);XfAjM{VHlfIUM9#@HPsu7L3z%3qA=OJqzALTSgn! z**GS~9C1;hRAo2By+T!QvCS6z9VMF!9u}%T3R%~JzoOZ#1!?r=g7;86&w}mnGgSQ= zEPJQxYshSU7ENZqus9lpxc&ylX=KE|28~|(Wq%IX_8Z7$zp{YXuq#oR{hu)$=oWk& z1*S!5`&E|>9A{;Mh_gN}_D$djH|zy)CI`;1NtAKM;dw|94U4Z$E{D`J!syJq(LAWT2sFCUv0ODPOw3f!+0EX4Qi(VVesUDW9|qd0R&PsJ^d`x}@G z#?b4G1u<~3xnMu)8HL6}p19SnUm`i?>A3V+)o56o&l1LE#1R`dS444{ap!>W+r20< zE-P^WM2+o(gX6NrO!%067+T|U=Chn9_|@~FGn4GZVh;MKqR%y~?ItfL6l+m`9A*mqc+`V`G<}8F9zvOFP7rS5`V4vNoH?F zCH?VV_rK2Ue8+bDEwic0u-`*U{CAcxfz!alh%HLT-(?cVr=Z#?@!#{8;27&4>gzP( z?{jkc@j`PW{`-Qk9W{?Eqg0Z8jQM|_$T>Bc{Q*pidg3291@48L{V$wG1FRDG#{cI< zm6XYcjQ_2Le`C3o=;ulPzx$s=f_)y`?SETVn6Jb4za1|oH9y~v?SES=UA=ZL=fZyo zG271?&>h}NBJOI`4edx#q3EBQk3=;oH+qjXCJDaN+bY7T| zuuc#-u458NSU-o>B-uZQXA{m6DH)>X2_;LJe&61E9qrJd#m(J-eSnse+wVuDWC zE{v+Tzd#F{1krDwMGIS!*TJX@Z9aXKutO+#&|ZqRN!Tff$LxF1QxaMQ@vQwBYTGr3 zGto=-*U@tm+U9UBdDV`?gqP4R#`o(^eR~$LI&Y!A#VEpJ9TWOY;)IKUo6JC-|8Y(c z_oGPz>txH(y@B)PE`XXf*7`MMil~O!Y`NGPcvncAWqspfPsMuq2NCM@E#*jnZ%pl z9<@`^j{;R9;R*Wzn&vdw^Y(M_QNU@kSM3(2Y@IiVws<>m8@f!ORuEn@@Dw~2*ygmR z8TbO`lR&*wJ2OxZ*9IB{m0||opo6vtijaM&8Q{xd0?k6pN;7ahd>lAijH7%rz{U!k zBdpqJ2H0AGouakM%>bJ#&?=g%$_%i*0=q(ysF^OHj zLP|Vu@$&}```5r8JIsPV85e@<4Ev{;-U1g3!m?MPvVqG4;kDnO4WAN(ZCKP0xZHdN z+FaBUXHjvS^&i+4xXmuqF8OsMrzk#F2rIb2~={-q7kC z89dF<5PUf*md6jz3X~jlxni$VSssXFi5yIlg!!IBC^9(T755k<=KV)RjEPd>1xyFQ zHLloNusnPvPcYrJ)Z*cijT0p2Q1DvI`xIEA91OtVmo3pKM(Ac5b;6;{P!z>)uoj<< z{$qu*(LI7US-}c$y`fKFcm}_2iDvhQR-+QZTP!ha2Se}DfNxldy~vjo%7=@Cw_1sO z*gZdV51Jx)hc)L6P`#m2v~2LZmT-B0=q*Zq-x3r4aOjiB9lYPsIT|XWhaPYwCqlXC z1Hq@A(hnM*QL4#sEq$4lWVM3iyc;~zW`XmZFR z)#;f#fbvp8yu&xx<(V4>wKTMc)#~;v+7D`F=o6S?f_+ZDH6bqKf`gvZcs(j3^d`$2 za;Tip0IN0ZQ2C))x?`_HZ46z8;SvmcmYfTn1tDSIeoykpL6w9qXZ;R3RCy?#Ejo&{Hc+7bFYZTY-MG}NxpwJiM_ zhw2PJQ~&=mfvwP{W}=vmH)2)M#j&p197TCPJU2C$4we;b7=) zw(m)g=w63H@3AA^;7}KaUZg+2?NEn9c`WZvC*P%^4b*bCLtP$vl$L$pBf9!kA^6S+ z{=lI=8(PT{e&|RZ3Gv?R;Ny-hM?=?e7(C%n$3n|Ex_{|VCqid4->)6&WavruiDw+@ zrqCU%?XwQ`jZiVE-#R7S9(tADea^A^JE7yGo_DBwLM!Oc|L`PkhlTfruI7OGvnPgU zEf0s@r{r6n1fDoP8hU_r_eG1I{8;D^ONfmYUV0*QHuJ?r`#uLPPltX*pT#@W3!(3@ zHJ3-HT#vNZLk-ljB3ks_w?bcF+h#@2Z$Qo7Haxwoe5Pw@2^6?En1bY78q9Ee+3um= zv3upY#Sk$d&F#Uj@mdWj8O;83$de0@l;(4l;xi`mtS9V*6|lS&=R zcE);zL&b(%)KcY8aiLKT#X5(I58ciNZgePrD1{E#6Yd3{0ktN?N6mwW9m$N)&GgHqZr`_&mJ{M_FTu;4w0vhGxza5rl8w$p@);+s zAoM7`bfeoBkA^AT&V7p`SrzJLm;IJ|*?Oeagnr2KzU`iO7O46V z9|H`2$1UdErqBY`=uY>XQl#w&b<>|ebB8vA+7|E1y{OfaW+^yXarZeXssQ5a`&^%rLy5&K(3eomawB{*Ec ziOWBni$(qzq&*3r#E+pHSfN(`50T>U?leN(Vxk9=DWP8TW+Yf|Vvx@6if0GH+7x@? zCl{tWr#_LYB(dT~{1Fbh*?atTkg&gsnPGPSvd59lun)r-vj@byXW2`TJbN&di3G2m zk4nxS5`=AU!lGgJaEJ}-H!P}~Jz}!F1U_~>5jA_<|7GeviV-n;!ZF!?j#eI=&&vT` z`v^QY`;gG^y!}0N-r46ZW0n_;S+wd_Y{C05#b+I-tj(p}>4m}K9BDkt3|Qj+a+ zQNN`7=H81^aE3;)ABYLR#O7Opk{%X5DK{*Zn)FNaN05uZ7*plSxvBnVXu*%Ml$pCm z9HR}}gXW!^CRAJYddSXQD^k4n4z%0cbfL}mhr|8G+zdhZZNAxKZl)lD_Mc$R+$=#P z+4sSXbF&4JY~yp-#@rl1q}Utb#<{tISZY|bXKtQrGt60X6%?GdNc;p8jeBC9jvz)N z+tXN*45NJaxyhN&bnei5N~w@|$$O}e??E9@LV+dRT@I~Fjb%SUH<{P&X(Wq7+*r;> zqk<)gLVTR;Y2$bK7C1ZTaRiP;1R_1tSf0!o?7RdY$M7u_;0_kOfW};Yf{A?rq5e^o z+gQ#Qjs%Yf1^W%M#ZJ^RvV#h)S(fvm`_O5okX^7q8lDs}z&UYR@&d+n=yf4<=BYx) z5+eJYlWw5&&MNtF}TN2KH^zN z4tLPPDQQJ1x<{bL<*8u;Z8lbX8=}E0f}Ret*#C?bpXWJMO|)kpS>ial%!k=LH0(J{ z4jbE8@-5VG-f%Q4_9b$~uv|3_=ymH@C`O+M z8I9-(=5?FdH_UCB55TA9bvw^MS{b$?Y#9e&yLsIZ4>V>&dU&J*)WvMrJxJ}L#N+4~ z=5#(c^BDd3#Bl)hD@KL`3LY+3z=d7GHS`2mDE z*mA)&{sKd@`V8~_1a?3^V~cA;xN!w08=R^5Hvcs9?5KU%?}!>lN(xfuFNc3cN=sCj z>1GC#N3npX>o}Kz=?>$A6pNAI9tEJ^5-<^%3nPBxEh*;F7vX=$ZwoijZ+xj2e>TbF z!NFX(nWOecpnpIfj}hj=v)NLwfqn&>9cG>l=x*kP;^h3xps&T2%HFy!1psMA2LU#NJvntHz!?M?02~IX z`2ZXzc?AIMwM9$lh!!)?$g2kH?-(Yn4l8O*cnDnrA#W#k9I@jf@f><{r5|-G551UZil?KPgh!ZH+aIy1X$TOKm`)&I^rpI&?lQGeRUHys z1{D=BAOhpZ<_C@of{GiC&I}GJE+Ydd^P%vJ13Ka;j{1Sy2cqLQ|Npt?z4u?$jW|B@ zJ>Ti)_Wj-WzGpw@J?Eafi#H@|Fp9gxF2pI;?W$l6yAIR8A`*2?`+cxxxtE=3vIEGI zm%S#ErDyC+1#32XVcmrLx`JifypSoIwh1p^`WV}026hgYGT2hLx-RIfP3B0N!_+o5 zL+C6zE*u zIW3chIZPiLaLPI5upm9_q|C{g6Ei&Q_6is!rusA8(V2r5q%xUEF$G0D4W$02r&4Fn zqkP8E2(}*?%*<=kpQ-w5asFWZkcizZ?ilkS^HS+q8pCY(m}V)Xk2y}GJ4wUbwlKTV z-wzFzph?V+gF@Pws6)M?%L;=!%!eHd795&dazvDa!@FI<9C3o%y=)%xc?xHX)L>vo zu68G0F(q|chDlG-vv4VNk3{fC$vK~d9(N9DJldC|moL(kU{LK#oqmKBEK_ufi}M&- zyv6P3qBFdWa@FEWLNHewyDoHqz%IZvN#{yQ`8cQt?PUu5Zi>j?7!)sYWryNn86`e<+t2+k= zsbCv-Td_?EewEsG8)-K-oZ#*{Y|#loo7;_}RIr^pQ`&wT4%*xz9HoNuxih8nV7_;U`|O>!9}zvOCsdZ&*=xG+;Qs)29luoYdu6g_(!A1bKEmG4AgQ( z`EJM}|I1_lU68>!|BRJ-N$k6rh!e3PE8Im1*##1psP8-(;?uCDf?eG0cDNJ&Cpc$a zgKgaH!xoh^iEkA_6>#Tnu61?O>3zT{4RdEo!*P~Oc{jkG7al;s1F$CZxW(zuD^}@2_NYW2Nf<=R_bu zFJ1~LYggW-e6c@QtJG^%ys)F$UoBk10{tzxZF!IyVkY4-oou>PBB_K%h5e>o$S zkwU(OMrM1iXCEo)=__{{u&OF%Ck$XTV<#_^HxCv{Fpx3B7BvcJ0uTes zwcMUIf|Upn7M@y ^ycDd2Xd=!j=(K+J9{dF*j*P^W+F(au5?6Amz|ZH{GEFJBR6 z%M4{YUgp%SE|1jqtsXcrp)l8A$nZ`%j;PN-tywbmNQ5QM2&#ov*hI1mEk?$etF1<@ zzCSuZ5|U_htwxw{jEqL7q{znjh!SS}r7F)MH^>;eL&T?yjF-wSbn(a-cc<`(2F-3d zlbV`lZc^RdQ`0HDusDVPIK=-s5aMhKUvNpeH&5fxyv?29t^1I+fTqB6y97aZ&ui0} z3dvz6nU7>lhrp82J=@OU?Yr$~=Gd7-I?u%6WAQWb(}iH?o1eLzXAZS9^Kb@P?PlKL zr8CP%O~`V(%N1OEHW`^px)>HhMBbvJoaBF(i#JY)D}ou&@0y-LXcvg6}14B+t$B^0m3Ja$nS!cqrIxrozG8Hi&G9gs>e#w{_>Pw}L?MijJ zUFy1~GiU92?aaqyd1VP{Xn%^%f~1{yFSIgnr8}{_C(RN492Auk8*zK8S#p{p%O6`n zx2x&+Y$n}w)rYv}xC%f>MVGkgtcULDQ`7UD+*Jv><#gD$l=Q<7rP^C%x2s!r=k_#p z*4;sjgt>6Fo#GB{g8pXu^6)JJ7xb3G{VpZbJ6k={7`3K2Mo6YOPb!(_d>@Qp8B5%S zp%k7`!nq1Y~t|J)vf`F>#0%7%^zYl>)H9l+r>5WRAkKMw z1{$a~8ouu73juNKIM`#jbP$FZYqMqQg&_vi@dEbEVt~j0_hwrVH?q~H zpNb_RMdK(&F1{@+2;Ixo%tyY|%#I+yMxk05t+h&7&IW=oddHY4sizG*nzdTzLFcwM+|({>a=tAZrwnvGVi zG6KC8(iMbNmkykR$6Op{5l5KRAf1gMEU#IWb;)(kIG5%%4rm;&6F5Yr8&wXGEuf%uZFp?S`)G;3&q-aHIGzcq7rM- z<5<2CAJ@YiE*}bUvHCesIl!zaboE`qEKby$)lyuD=WPhj+ps#=U&uB(13)F$s!7XY zqj9B%_|a5HL*A-x*<)L!Hj;u2WN~9SerlOm`&ER1%DN;shX3Q39j@hZu~bH7)v8Er z2`>R`j5a5Pi|G7pv>DiREY4ANB)qiHs0GMt$c8lD#I(09h$gQi(WW4@wg$mkQ z;ysreM_puFwWP59v@=Ajtc^Ev=uKEbjyieVl`Q&VxVKT?TWL17?r(ogC4JH@+|DcnzGTG)T$~L z#Em{gF!e$XhOv-0LtUyCYq4e}yD!9LW^B$a|L$r8bwS(QS+1^khoiJvad~@dWhrWn zp*fD#%K11V+E$n?3!nWn%5M;^$7)Kz>r(z!QERh5v}N$+s;HywI$IDHt4XyM_A!t` zyAhyi9Vyjd{mU?8adL_-{gIu*e+(XRD~1{0yk+x{sKWfAAe?psfFfk3tE!9 zn2cj#E^rOWtU3c{(XlFAWpV&Q?kpTwQ^S#L9xFl^Kq{rA%FQ1#Q!4I9ZPNb4UiK6U z7zf51XzmzRVD4xFExfuLO|0Kwpo>i)hl7x9G_pnJ&0A8MFh3uIR`blWHw|&0FJZL@%{45R zv!l%wab!hjcj7~i1+Lb{M_!MGr;raUS)E}R-gfrZq4PE@Uo+sXygP3RpqXuy+bWAz z$(j2ymrqU6_a8S~G#S!S9i8YmJKh>|<6Ky1G^&ZAc5N6%Jetk1ueD8$IZ|ZHE*l)j zFkqwjX7g6>(C$Zh z3FfCSaZARSHgpE2Wl^k{b4F;vwEyU94M zuIcj(O{cqLY2{)1c=gO{XP-P=3QWu-F1#>5#VjAz1Iq}lS_^h8viETbvYAaX%iv|1 zYa8CO#QMqgnw1EiDsG!1TP1SXc9;Fz&g9kFiq&gq;M|C8UNI*splF;B=8z^i&T<8p zp6wUlol^$uwBd%02pL6x35(vs9*5nI3f&~Q48Vo7vp4zLIF?Xi-8i?M?1syWvb; zFs|F|u6bFCmY;gtb(1xDy*{YB?t&jlVn3MpL_eGG%DGj$B~<8{Y1%` z4y#&}JSKi|?$|oxkxHz3n>ny>Z8aboo-8lc1SV+NM4tn*i1}?+4xtjbw6!!ytdGj?i8iM6!etz%PWbhdqpX#>8ysbHL4uR1 zqbZ>{+|uG%n|Jh_vHXOd=njRR-U@9D7JbXjp59Tej~07ZfkyKqJ-u5-dwTJ!Mu961N{`s5n`$!tj?$tz=r=KByqW7^;IJ3V<_XHz67t-Wpmp z!p!sc-;L|GZMb0FwxPH*G5ZbK>^UICx-vYpttaq$rJik#xyz*ra%HP~STJd{-0~i) zBD#jsb)@7XS1O26+9A6JUTR?Fz+M@T5@<76ETNU-H82|yrsB;gmS_OrimN5ww^tZr z3yiZQXEsE|Wh+|!3en8QuwIBUE}Q32f%i)lqs2$uxe&l>GQoz`p7i2WEeO)sWH6RB zGb6mjM5oQ|%Ld`5EoYn?ZVxJ$Ko&;ElNGkQ8`aDw+TD0#!mkMxbTPSj#RRVz)d!Xm z{c0yVT)1NsuUq1vP`BSY)G5K(FbX+os4T;(^V~DO$viYP2ID|w^A!>7N1DR_Lt}ZS1D0gmz z^wrB??JQdzmfn*qY|Qx@`|F5d3{4yBMsXX8hKX#FRX4GSr-_sFv>lsw3=eH^Cej&S z`z6KVc7vh2-olKeSanPFc8&#$$rq{$<;#g7r%@FWuumZYxZ{&+Od5o+;le9>(^}>K6PNmy7gz{Ka}C3r^-cuIg5;EKarPUQ%93&j;4iPM35 zQX$q!2eV=(q>(X`EW~k_%V?sKb_JrxbCUy5y6sK4JjTOM;?e6 zxHCYG7#?TbG=?rkff+0Z#keDD|#gE+@-G#7uT`(y5S{>+Z!*K`~= zJB|-@9KWyQ_-=p9ZZ#M6`9R0{?{*yjtmAl!6e-ik?6s@T*vXH z9mh9x9N*n>e1FIB!yU&@bsYbu<9HVPsCP^c-a8#T9(Q$mIKc*>}i`Mexj`QE}$222z*}pL9PVUl-=U+B$_5Mi!|Q4X~!Z!|f~rh4$tyObYyoz9|2=yW{0sg7s%y_9D@iD!(bl452E zDL>_MVZM@*Xj)-$xiI}O;8tNv^*VIl%}Vt}%qK!DFN5U%NWOv00N^%a!(*GyZ6dFTcq@5j#M{Y? z1dh+=q}+4Mcm)r64W)s5ASQJ}#ChyeYa<>b^U)HxGPcx74ud+GPXNKSu%(z-xP91C zr#cMYNcQz|8QIU5E+_lJ@CuP1AP?dn+_l(@d2ISq$m=Qbb!T%h9Gkw=8Kj2S8Fc7o zSf(=@BIEhOu715fX9)8T!#_CAwa5&cobm0JrzxL?Jvq~Ze{gJS^ZgVG53f0G03gL#ak(sulXQt;G z&rHJyJu}{?JTtBzd1g>Q@q8rq>oE>9%pUMK8B<+O`#Js_yYhj~G0-7re1G9Am426E zPtLpw|KRwn9OcVAXM8_mQBppDJvl=KIC`UemFJA_U+f-~uf?96@qLZWnsPQ{vX#|Y zUOtFDIph1_PA_L!k~2&I+-_`^$30|(xeg zd1k$^UMc4=gedWvH2i}jTRM4ZOda-V*N>nx#dC%R1CHH*ey4f1ys&wh-!nXK#eTbI z4vQCfX3?<@sDCB)t__2CgL!P>OeHfRaAeE-EHLFv8`Xc+;`m zQY>INnnKeV5@vo)J}i8Za41|AZVEHa7S7ee*9qS){2}4b2!CGqYr_91{Fv}l!p{iv z`rFboTX>D|8N#m?4u$s!yJa|E{(MiDh5r%ZdxXCv{LjKa7XG!c`GOM*Kxup2oDJl3ulFE!fz6Om+(!(cL;x0 z_guf^JYvIM+N%~g^ZxX&xxG3BdzD)RP;hTjY7XGgAZ-l#GysSJA5k6A*IN{d{ zzaV_jK}k4=3gfLqarn!HHwj-PJSMzX_;TS72!BfWKH-OizbpK#@E?R{Vz9Mxc$x50 z;Wfe=gtrOr7Oo1vQTQt1JB9h7B1=Oz#zy1Agck|-37;f z9}54w@Uz0t3D3kl#p>}0;bVnY3J(guS(vYFws5{6{0-r!gr5^mV^U&%UnP8k@VUYl z2{668kgg+#h zcnW?ywDivrUMPIL@QK1_3GWcj312My7s6Ky-y(dc@aKg2^$knsKMVg<_<7-O%*{=I zuJBR9y~5`R7ljW9-z5AM;U5YAO1K*r1D1w)!bc1D3!f@{uJDDzqr&6DZxiN+5G`K5 zgvpq%NjK&fHI2U^%=eL+{NIJ27oIvd(U~v2NO(Z_G~rFc7YUCE?-jmW_&VX+h3^)= zSNH+p$Ay0+{A=MZT%TFqXA3VBUM75!@J8YDh4aGvG?;~dnea8jw+i1S{CVN82|p_Q z1L0?d{~$aQ*PWJzmkBQwUL(9gc$@HU;i~W(g|8C6QTW5c_XvMk_*=qH3ja*_1>tEJ zLo8p12_GZOPpp{Eknpf@R=6hoCgFDp-z0p8@MnejO(2W+5#jF%|6KTY!qc%BWPTS2 zFBV=YyiRzF@N0$l2=5nur|@42e?a(C!uJV3B>Y|Bp9nuE+>JGR%hx>NqlNp0PZd5_ z_(I`PVSfJC!hf6adxUQn{=@WEJ+vUHv=d^gqzP5uwUj|xwDd7?8%c%ks|!fSgzpgky72SD^LvuGjuk#l_|?LB;Y)zZ3oYgdY-qMEE;o%z@~h7WvPFe@XUn z&0Li5@xmL)zD$P6xZb9_0BpZaR4Di5d9m4bIr|`#xKTY=G ze?jD568@U#JVf^C|0j_@A^QI+{3Fr%g~)#+`~umBnSN!$GldToUMRdoctH3J;fu&v zXQA6I@_oWrlCh>n_g<0TDSWT+SA`!I{x>q#I_Z8Y@?Q&2J0{8F65#>i^MubA4uzY- zR}247vd{0GBEMhwG2y4lz70G>#(EjuufXIj$Nl!_ZSP4UUnk7_a83Ur;nxWlgiFG_-@yE~ zgx^K>_Uhfj?-jmT_=Cdkw?*&w6C(eNFh56O;Xfq&sPN;$|6BN%!oL;%4`F`B!oojV z_&DKS;Z?$Gg-;hgL--uw&B8l`FBIM_%#ZL{+W2V=pcCUwWUtchX@p!$Ia^Lo72W(#C?0}!At)kpBFxH7eFxdc`)=VIJu{s53*Y4#KhXuB5x&VZ^zUp zrx7`MH+ZHne@CV~2RT2~XuQO86Y>G!HJ&-fp5pnf;PsvlfY0@O6L?toHJ)#Qe8ltn z!FxP^65JAgljnOOe~0k9J>LiUjl%Eu{2=6a34g}(HzB`Q_^Y135BbBwk9vLz@+Un1 zAMlTapYhE53ZEC|T$p7z9r78%^E}UooS%oI4tcTX1>ip66Fl>N!$ILQJuilQi||h2 ztmgsfyk5BJnfD>`w{oU|{5H>nVE%4SIqyll-ZP)UxmEZ>o_SB=-NN^H=6yqd>v;@( zzh}-1{!#c*&rQgm@XULApYqK6B!A}l&EVg7=6%1v6+Q%Z;v{cZ4;NnOnf-Bz@PIHs zc}~B)gj_FtwrASdt)8(Y7o6|82OJ9XyQvI^@`~q0;Juz{_ulMz37Gc)P@jCQFn{-_ zoObW8gg@%}1jz3ZzSr}KkbhP9A>r?MJ{daS^L!fk$HLEe9)$dP&u4&nJ;d~!37#Q5 z+w(@q7YO$VFBR?+K0)|YVSZ?hah-!Ow|L$J-X%Qdc`M}Oo@vA1BFy^@7!K|E^`0*P z-|G1y@P|C_27g@muRV`IexK(&`0K*o^jw7eanF0e|LXZl@K1z)>G=TUFL=HNoJK!m zI>`qKzk=-N7`%tUc$w!k^jCO>_kiCqq7K{YX`VU$Zt%?c&N-f!frmY_ja}gR6fj-t zuxO9&6{L7E2gk80#X$;=yA%g{xE^dNb_(yzFVkdxoxb<$=3U+|$20TGxNYvl_GHZZ zGu}-`yQC{)Gu|hBIT`t(yB3@At->D>zDM|r!Vd~RF8o6>GY9u9Hsjw5PlIhTIqkb~ zkMJ_#wZa>Ow+hqFS~#?)#w}smM4L|@5WZ13=xgq;;N=|H;{_{jKW5Y@l(T(hyrQ{* z4=u!x3HG&#Cwm0TJsUcAuf-j>og(rgBOjT#T)3^1e1 z`&L2D>`^;D73;oOt|syOVyb^MET8p1F}U`Eaw(oHRw<S5RmkJ)U4EmzauJlFG zx|E?t(CU1MIKZnV>+D~Ds_TIwnz*~?pMGf5d>7@RyU(6`{M6)lkOy#|9E+Y4>Ic*R z6m0az@m0ec#PN~X%u$bS1vc^^Ho8X<9_y!Lqx*CV~39yDoTQ*sK@A7^Iv8S81C&@3( zvZe14u!eUy55(EwV~~+qR&+0>_XN&O!}d~oKY$LyW4+KZyv}mr`TvWJdt4ehcMJ~j z(^u@QK3;%C>th%4dkSQ%k3;-9hCd7b8J@*U8_xT2%`ps~KiTV9ykp4J`KE-u#mluh zjrT^xdn?W}-nm|n@wzoR>@D6eB0LKlViyMEb@%6EZ|D92dYAKrcNXv4JK|kc#-}_{ z=!}<3!PH~CpMoB}TX_5&?sYgeXW@OUBfNj9MbC0EJWeYtyl;1e_kD!Nxv4n|?^_+= z-2*G}5hyV{PAM!rw{~ZqvG2ir;0igm@P6G9-mZ%=kAjThnXO=cGhI=BEr^dGyyK3w zqvYIF=rQa;f9i$?${7JMJp5c6r*A<=c#mMQW5^me^pFJSUe%#@*lNuCAv0%siy_l^ z565KkSzm8GP_}Y8sUy4xVCV)dFzhY7)g9qYL*-tM^UTL0Y!;qdpTx1b8Ed0w67AT+ zJG&#i0|?LR?HDLqcvK9ZJ=RXprkOrgi-lM5;SKlz?mi<3 z?qJ1w$MCq}v#xG$dOO1CxzA3$w?I$2b?|oRA&IfG^j+`6vlCx{-cmcj-t@Ts%Crvp zbI;$B*q7rWvwYm%5#GNcyboF+&@;THBfM2G8q{OC^h$WthidE&WRV6$#{3_Jjq%RL eMw>3pGI+3nH7D_C@$$M^^&Wp;5ZpP5-hTp##mXH3 literal 0 HcmV?d00001 diff --git a/examples/atclient_esp32_source/main/CMakeLists.txt b/examples/atclient_esp32_source/main/CMakeLists.txt new file mode 100644 index 00000000..9759a0d3 --- /dev/null +++ b/examples/atclient_esp32_source/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "main.c" + REQUIRES mbedtls atclient atchops +) \ No newline at end of file diff --git a/examples/atclient_esp32_source/main/main.c b/examples/atclient_esp32_source/main/main.c new file mode 100644 index 00000000..da808d31 --- /dev/null +++ b/examples/atclient_esp32_source/main/main.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include "atchops/base64.h" + +void app_main(void) +{ + const char *src = "Lemonade!"; + unsigned long srclen = strlen(src); + + const unsigned long dstlen = 2048; + const unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); + memset(dst, 0, dstlen); + unsigned long dstolen = 0; // written length + + int ret = atchops_base64_encode((const unsigned char *) src, srclen, dst, dstlen, &dstolen); + + printf("atchops_base64_encode: %d\n", ret); + + printf("src: %s\n", src); + printf("dst: %.*s\n", (int) dstolen, dst); + printf("dst bytes: \n"); + for(int i = 0; i < dstolen; i++) + { + printf("%02x ", *(dst + i)); + } + printf("\n"); +} \ No newline at end of file diff --git a/examples/atclient_esp32_static_components/CMakeLists.txt b/examples/atclient_esp32_static_components/CMakeLists.txt new file mode 100644 index 00000000..2f7f87a5 --- /dev/null +++ b/examples/atclient_esp32_static_components/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.19) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(atclient_esp32_static_components) \ No newline at end of file diff --git a/examples/atclient_esp32_static_components/README.md b/examples/atclient_esp32_static_components/README.md new file mode 100644 index 00000000..9aa42a1f --- /dev/null +++ b/examples/atclient_esp32_static_components/README.md @@ -0,0 +1,91 @@ +# atclient_esp32_static + +This is an example of how to use the built static libraries from [atclient_espidf](../../packages/atclient_espidf/README.md). + +## Consuming AtClient/AtChops as Static Libraries + +You will need to have built the static libraries from [atclient_espidf](../../packages/atclient_espidf/README.md) before you can consume them in your own project. + +Once you have that, be sure to create the following directories in your project: + +- `components/atclient` +- `components/atchops` + +And in each directory, create a CMakeLists.txt. + +`components/atclient/CMakeLists.txt`: + +```cmake +# register this directory as a component +idf_component_register() + +add_prebuilt_library(atclient ${CMAKE_CURRENT_LIST_DIR}/lib/libatclient.a REQUIRES mbedtls atchops) + +target_include_directories(atclient INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(${COMPONENT_LIB} INTERFACE atclient) # add it to the component library +``` + +`components/atchops/CMakeLists.txt`: + +```cmake +# register this directory as a component +idf_component_register() + +add_prebuilt_library(atchops ${CMAKE_CURRENT_LIST_DIR}/lib/libatchops.a REQUIRES mbedtls) + +target_include_directories(atchops INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(${COMPONENT_LIB} INTERFACE atchops) # add it to the component library +``` + +Then in your `main/CMakeLists.txt`, be sure to REQUIRE the atclient and atchops components: + +```cmake +idf_component_register( + SRCS "main.c" + REQUIRES atclient atchops +) +``` + +Your root project `./CMakeLists.txt` can remain the same as any regular ESP-IDF root CMakeLists.txt. + +```cmake +cmake_minimum_required(VERSION 3.19) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(atclient_esp32) +``` + +## Running this example + +This example requires the ESP-IDF toolchain to be installed. See [ESP-IDF's Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) for more information. + +Ensure that your ESP32 is plugged into your computer with a micro USB data cable. + +1. Get IDF + +```sh +get_idf +``` + +2. Build + +```sh +idf.py build +``` + +3. Flash and Monitor to your ESP32 + +```sh +idf.py flash monitor +``` + +4. Your output will be similar to the following: + +```sh +atchops_base64_encode: 0 +src: Lemonade! +dst: TGVtb25hZGUh +dst bytes: +54 47 56 74 62 32 35 68 5a 47 55 68 +``` \ No newline at end of file diff --git a/examples/atclient_esp32_static_components/components/atchops/CMakeLists.txt b/examples/atclient_esp32_static_components/components/atchops/CMakeLists.txt new file mode 100644 index 00000000..c8b1c2dd --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atchops/CMakeLists.txt @@ -0,0 +1,7 @@ +# register this directory as a component +idf_component_register() + +add_prebuilt_library(atchops ${CMAKE_CURRENT_LIST_DIR}/lib/libatchops.a REQUIRES mbedtls) + +target_include_directories(atchops INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(${COMPONENT_LIB} INTERFACE atchops) # add it to the component library \ No newline at end of file diff --git a/examples/atclient_esp32_static_components/components/atchops/include/atchops/aes_ctr.h b/examples/atclient_esp32_static_components/components/atchops/include/atchops/aes_ctr.h new file mode 100644 index 00000000..ca9eb607 --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atchops/include/atchops/aes_ctr.h @@ -0,0 +1,31 @@ +#pragma once + +typedef enum AESKeySize { + AES_128 = 128, // not tested + AES_192 = 192, // not tested + AES_256 = 256, +} AESKeySize; + +int atchops_aes_ctr_encrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned long *ciphertextbase64olen); + +int atchops_aes_ctr_decrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned long *plaintextolen); diff --git a/examples/atclient_esp32_static_components/components/atchops/include/atchops/base64.h b/examples/atclient_esp32_static_components/components/atchops/include/atchops/base64.h new file mode 100644 index 00000000..d1a9c227 --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atchops/include/atchops/base64.h @@ -0,0 +1,25 @@ +#pragma once + +/** + * @brief Base64 encode some bytes + * + * @param src src bytes that you want to encode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 encoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_encode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); + +/** + * @brief Base64 decode some bytes + * + * @param src src bytes that you want to decode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 decoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_decode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); diff --git a/examples/atclient_esp32_static_components/components/atchops/include/atchops/rsa.h b/examples/atclient_esp32_static_components/components/atchops/include/atchops/rsa.h new file mode 100644 index 00000000..bffb64b4 --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atchops/include/atchops/rsa.h @@ -0,0 +1,84 @@ +#pragma once + +#include "sha.h" + +typedef struct rsa_param +{ + unsigned long len; // length of the number in bytes + unsigned char *value; // hex byte array of the number +} rsa_param; + +typedef struct atchops_rsa_publickey +{ + rsa_param n; // modulus + rsa_param e; // public exponent +} atchops_rsa_publickey; + +typedef struct atchops_rsa_privatekey +{ + rsa_param n; // modulus + rsa_param e; // public exponent + rsa_param d; // private exponent + rsa_param p; // prime 1 + rsa_param q; // prime 2 +} atchops_rsa_privatekey; + +/** + * @brief Populate a public key struct from a base64 string + * + * @param publickeybase64 a base64 string representing an RSA 2048 Public Key + * @param publickeybase64len the length of the base64 string + * @param publickeystruct the public key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_publickey(const char *publickeybase64, const unsigned long publickeybase64len, atchops_rsa_publickey *publickeystruct); + +/** + * @brief Populate a private key struct from a base64 string + * + * @param privatekeybase64 the base64 string representing an RSA 2048 Private Key + * @param privatekeybase64len the length of the base64 string + * @param privatekeystruct the private key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_privatekey(const char *privatekeybase64, const unsigned long privatekeybase64len, atchops_rsa_privatekey *privatekeystruct); + +/** + * @brief Sign a message with an RSA private key + * + * @param privatekeystruct the private key struct to use for signing, see atchops_rsa_populate_privatekey + * @param mdtype the hash type to use, see atchops_md_type, e.g. ATCHOPS_MD_SHA256 + * @param message the message to sign + * @param messagelen the length of the message, most people use strlen() to find this length + * @param signature the signature buffer to populate + * @param signaturelen the length of the signature buffer + * @param signatureolen the length of the signature buffer after signing + * @return int 0 on success + */ +int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, const unsigned char *message, const unsigned long messagelen, unsigned char *signature, const unsigned long signaturelen, unsigned long *signatureolen); + +/** + * @brief Encrypt bytes with an RSA public key + * + * @param publickeystruct the public key struct to use for encryption, see atchops_rsa_populate_publickey + * @param plaintext the plaintext to encrypt + * @param plaintextlen the length of the plaintext, most people use strlen() to find this length + * @param ciphertext the ciphertext buffer to populate + * @param ciphertextlen the length of the ciphertext buffer + * @param ciphertextolen the length of the ciphertext buffer after encryption + * @return int 0 on success + */ +int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned char *plaintext, const unsigned long plaintextlen, unsigned char *ciphertext, const unsigned long ciphertextlen, unsigned long *ciphertextolen); + +/** + * @brief Decrypt bytes with an RSA private key + * + * @param privatekeystruct the private key struct to use for decryption, see atchops_rsa_populate_privatekey + * @param ciphertextbase64 the ciphertext to decrypt, base64 encoded + * @param ciphertextbase64len the length of the ciphertext, most people use strlen() to find this length + * @param plaintext the plaintext buffer to populate + * @param plaintextlen the length of the plaintext buffer + * @param plaintextolen the length of the plaintext buffer after decryption + * @return int 0 on success + */ +int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, const unsigned long plaintextlen, unsigned long *plaintextolen); diff --git a/examples/atclient_esp32_static_components/components/atchops/include/atchops/sha.h b/examples/atclient_esp32_static_components/components/atchops/include/atchops/sha.h new file mode 100644 index 00000000..786522e5 --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atchops/include/atchops/sha.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + ATCHOPS_MD_NONE=0, /**< None. */ + ATCHOPS_MD_MD5, /**< The MD5 message digest. */ + ATCHOPS_MD_SHA1, /**< The SHA-1 message digest. */ + ATCHOPS_MD_SHA224, /**< The SHA-224 message digest. */ + ATCHOPS_MD_SHA256, /**< The SHA-256 message digest. */ + ATCHOPS_MD_SHA384, /**< The SHA-384 message digest. */ + ATCHOPS_MD_SHA512, /**< The SHA-512 message digest. */ + ATCHOPS_MD_RIPEMD160, +} atchops_md_type; + +int atchops_sha_hash(atchops_md_type mdtype, const unsigned char *input, const unsigned long inputlen, unsigned char *output, unsigned long outputlen, unsigned long *outputolen); diff --git a/examples/atclient_esp32/lib/libatchops.a b/examples/atclient_esp32_static_components/components/atchops/lib/libatchops.a similarity index 100% rename from examples/atclient_esp32/lib/libatchops.a rename to examples/atclient_esp32_static_components/components/atchops/lib/libatchops.a diff --git a/examples/atclient_esp32_static_components/components/atclient/CMakeLists.txt b/examples/atclient_esp32_static_components/components/atclient/CMakeLists.txt new file mode 100644 index 00000000..aa5eea58 --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atclient/CMakeLists.txt @@ -0,0 +1,7 @@ +# register this directory as a component +idf_component_register() + +add_prebuilt_library(atclient ${CMAKE_CURRENT_LIST_DIR}/lib/libatclient.a REQUIRES mbedtls atchops) + +target_include_directories(atclient INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(${COMPONENT_LIB} INTERFACE atclient) # add it to the component library \ No newline at end of file diff --git a/examples/atclient_esp32_static_components/components/atclient/include/atclient/at_logger.h b/examples/atclient_esp32_static_components/components/atclient/include/atclient/at_logger.h new file mode 100644 index 00000000..c6be04fc --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atclient/include/atclient/at_logger.h @@ -0,0 +1,9 @@ +#ifndef ATLOGGER_H +#define ATLOGGER_H + +#include + +int atlogger_log(const char *title, const char *message); +int atlogger_logx(const char *title, const unsigned char *bytes, size_t byteslen); + +#endif // ATLOGGER_H diff --git a/examples/atclient_esp32_static_components/components/atclient/include/atclient/atkeys_filereader.h b/examples/atclient_esp32_static_components/components/atclient/include/atclient/atkeys_filereader.h new file mode 100644 index 00000000..e02fba54 --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atclient/include/atclient/atkeys_filereader.h @@ -0,0 +1,59 @@ +#pragma once + +#define TOKEN_AES_PKAM_PUBLIC_KEY "aesPkamPublicKey" +#define TOKEN_AES_PKAM_PUBLIC_KEY_LEN 16 + +#define TOKEN_AES_PKAM_PRIVATE_KEY "aesPkamPrivateKey" +#define TOKEN_AES_PKAM_PRIVATE_KEY_LEN 17 + +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY "aesEncryptPublicKey" +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY_LEN 19 + +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY "aesEncryptPrivateKey" +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY_LEN 20 + +#define TOKEN_SELF_ENCRYPTION_KEY "selfEncryptionKey" +#define TOKEN_SELF_ENCRYPTION_KEY_LEN 17 + +typedef struct atclient_atkeysfile_entry{ + size_t len; + char *key; +} atclient_atkeysfile_entry; + +typedef struct atclient_atkeysfile { + atclient_atkeysfile_entry *aes_pkam_public_key; + atclient_atkeysfile_entry *aes_pkam_private_key; + atclient_atkeysfile_entry *aes_encrypt_public_key; + atclient_atkeysfile_entry *aes_encrypt_private_key; + atclient_atkeysfile_entry *self_encryption_key; +} atclient_atkeysfile; + +void atclient_atkeysfile_init(atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_read(const char *path, atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_write(const char *path, const char *atsign, atclient_atkeysfile *atkeysfile); +void atclient_atkeysfile_free(atclient_atkeysfile *atkeysfile); + +/** + * Usage example + * atclient_atkeysfile atkeysfile; + * atclient_atkeysfile_init(&atkeysfile); + * printf("done init...\n") + * + ret = atclient_atkeysfile_read(path, &atkeysfile); + if (ret != 0) + { + goto exit; + } + + printf("done read...\n"); + printf("aes_pkam_public_key: %s\n", atkeysfile.aes_pkam_public_key->key); + printf("aes_pkam_private_key: %s\n", atkeysfile.aes_pkam_private_key->key); + printf("aes_encrypt_public_key: %s\n", atkeysfile.aes_encrypt_public_key->key); + printf("aes_encrypt_private_key: %s\n", atkeysfile.aes_encrypt_private_key->key); + printf("self_encryption_key: %s\n", atkeysfile.self_encryption_key->key); + + printf("writing...\n"); + + ret = atclient_atkeysfile_write("/Users/jeremytubongbanua/.atsign/temp/@smoothalligator_key.atKeys", ATSIGN, &atkeysfile); + * + */ \ No newline at end of file diff --git a/examples/atclient_esp32_static_components/components/atclient/include/atclient/connection.h b/examples/atclient_esp32_static_components/components/atclient/include/atclient/connection.h new file mode 100644 index 00000000..4d0d2351 --- /dev/null +++ b/examples/atclient_esp32_static_components/components/atclient/include/atclient/connection.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#define HOST "root.atsign.org" +#define PORT 64 + +// #define HOST "245b44d4-a4bd-5f33-b077-c559f956486a.swarm0001.atsign.zone" +// #define PORT 1722 + +#define ROOT_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" \ + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \ + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" \ + "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" \ + "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" \ + "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" \ + "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" \ + "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" \ + "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" \ + "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" \ + "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" \ + "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" \ + "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" \ + "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" \ + "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" \ + "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" \ + "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" \ + "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" \ + "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" \ + "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" \ + "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" \ + "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" \ + "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" \ + "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" \ + "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" \ + "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" \ + "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" \ + "nLRbwHOoq7hHwg==\n" \ + "-----END CERTIFICATE-----\n" + +typedef struct atclient_connection_ctx { + char *host; // assume null terminated, example: "root.atsign.org" + int port; // example: 64 + char *cert_pem; // assume null terminated, example: "-----BEGIN CERTIFICATE-----\nMIIF..." + void *server_fd; + void *ssl; + void *conf; + void *cacert; + void *entropy; + void *ctr_drbg; + void *saved_session; +} atclient_connection_ctx; + +void atclient_connection_init(atclient_connection_ctx *ctx); +int atclient_connection_connect(atclient_connection_ctx *ctx, const char *host, const int port); +int atclient_connection_send(atclient_connection_ctx *ctx, unsigned char *recv, const size_t recvlen, size_t *olen, const unsigned char *src, const size_t srclen); +void atclient_connection_free(atclient_connection_ctx *ctx); diff --git a/examples/atclient_esp32/lib/libatclient.a b/examples/atclient_esp32_static_components/components/atclient/lib/libatclient.a similarity index 95% rename from examples/atclient_esp32/lib/libatclient.a rename to examples/atclient_esp32_static_components/components/atclient/lib/libatclient.a index baef866a43fa24a363a30fc69a00d4f1b67f6d85..4cf4208d2c5929e658aff6a4dab133b923101381 100644 GIT binary patch delta 2074 zcmZ9M3rt)^6o${--DP*V3+1s0Mc55&i-{XrSm?H5>!YdF1dP~%8j4t{MrfO;Ek0su z)@s3q5KWx=s!dv&t|EyRT$Q3UF59A!@Mxog@zJ8hN;QqxRO6!%|8p6(S0*_#-<2D2+9TD6j(E#bEky3aZrf5Kx|mCpWpq5t zig%Q34P+1%G}?$VvW21bc%(h*6E{eI)GO*_c{E2f%Z6xfwQrXTs@;aD(}Ofd*O~w_ zI?jrOpJgLqFFJQD+0YKLcG+Sjqk``sb8od9u_<{pnwe(vB4}*yd6v}*l4qhhT8UR) zj$UoAoQq0N|5evE#FfTs*Ys-0HSS7ij{Dc7Aj2)$B#-ugt6j{MZ84wL>5<(ruecyj z$1>3W9IFY1jpcdAFTuotTl0yQZ)tjxM0lk$kI2Cxv?5n3!r}70I8Cv!_=L{0x;2Qb zhplDBQ#b(;et3N%hU(=zcWl9|wF0wkh(5%MFR)7yKEO|jy8}BOY@$2FTTXt1S72?h zl0CQvF=cxJT)LR$-A4Q@=Hsxyb`tNoia}mMlnGk_rr)vCKqHBmTy%0O?~v^o1WHG7 zhT^$2Uab|G_QFc1GvV7Kvj*BikHXK#^|FtsP;9c{#TaTWi9S08pT&d^xx-(b$iBo_T-yof z8AO}mD~S8B8|^V9xEIU&|3tWsAGYGB*O zC_i@_MrNIB3=J1UZWKFL)GlUdDljtZ0d#0BF!om})=St+Bw_5YQmmJ4v6Q4J60=~2PoarVmo#QlP;X6%%#-4XPBHdc^Hoi%LyX4kq;sFznnD{4V z{M5MpYVuz)v11A6d^Oo8JUy#?qS6`SiYc-am0>03pyJ2Ds}=L#Wv${iaJgdspKLVo zCdFJa^@`iU+Z1hZdD)c~x z(|PWNjv|Zv=EUmah#mUcRPwm(N1`F3IMKO4G9KIf$tiEfE(=j^zOa-K(Y?j8y5A?V z<(7W$V}Sx9PlJHlkPcUcJe6Ir61i8~tj}?B$%3TfwR})ByEoyev=82$$k2#i+XjWW!ETv-`kq>UBGjq!zP|IBJ+V|;!%&zscqhN-Mi zL!A+)wI&r`H4lol`ZlsmNOrjOZG=(+zR;6$Q#7S$6%uUks_kX9W8rFM3>nMiENz0 z@H?}Rj;=q94T`6$2JkXOblWOq*1>97@fJ29qK)hD?#1TH=B6E3MffCMMRq4EzQd!7 zXggmewgj49YN02^Njq=%eb^CL^}$@^#w|y|H49jNM$g~IdIT2OO`^-mC+o#I=fReO z=`MB}*hS)k%)IzZVY_8N5;cQ39udywyG2mxFs$bHJjC`(&!r>bCd3cn?Y-wCsu1-S zJQzcRDczSw!9!T^0d&u1wPsFft?*lgpMoDE%?CI%n-+d#+3CqnB>B|G^(5jH>_&*2 z;t!<{P+fT(u`$HTF9W&(!J7bXyV7FbNl0sfR~w`9F(g}Abr-&06jbfO^Y!Y^tbPYc*A0N&&n>2fl92{ON=F8lX z%OV=PJ>t@|2_d)J-7)R^U}5+u>Lv%elsf0EFV0!tnzNpqv!0o=&cUH12iGt8|5~Gm zVts#QX>!|)M@D8A$*a@v$f;4M+&ANleLAzmTy+)~xjjVqBIsMiTo1 zT1+BGgmmVc09;Yq90%! ze?_rlIY-f$v<3%)mVcP8j$n0~0>0WPQ&pXX`g!YDm=go+%JH<|lgau?ajSkLw2h z*}zi<7RCSR(+yn6Z2chLCkn)x-U^?nw}-h;{AolhP@j3Z;yvJ61Fu)yhIpNUwF|Dde2sf{H%Wg2%<$< diff --git a/examples/atclient_esp32/main/CMakeLists.txt b/examples/atclient_esp32_static_components/main/CMakeLists.txt similarity index 58% rename from examples/atclient_esp32/main/CMakeLists.txt rename to examples/atclient_esp32_static_components/main/CMakeLists.txt index 709b75c8..cb56fe94 100644 --- a/examples/atclient_esp32/main/CMakeLists.txt +++ b/examples/atclient_esp32_static_components/main/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( SRCS "main.c" - REQUIRES atclient esp_wifi + REQUIRES atclient atchops ) diff --git a/examples/atclient_esp32_static_components/main/main.c b/examples/atclient_esp32_static_components/main/main.c new file mode 100644 index 00000000..da808d31 --- /dev/null +++ b/examples/atclient_esp32_static_components/main/main.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include "atchops/base64.h" + +void app_main(void) +{ + const char *src = "Lemonade!"; + unsigned long srclen = strlen(src); + + const unsigned long dstlen = 2048; + const unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); + memset(dst, 0, dstlen); + unsigned long dstolen = 0; // written length + + int ret = atchops_base64_encode((const unsigned char *) src, srclen, dst, dstlen, &dstolen); + + printf("atchops_base64_encode: %d\n", ret); + + printf("src: %s\n", src); + printf("dst: %.*s\n", (int) dstolen, dst); + printf("dst bytes: \n"); + for(int i = 0; i < dstolen; i++) + { + printf("%02x ", *(dst + i)); + } + printf("\n"); +} \ No newline at end of file diff --git a/examples/atclient_esp32_static_no_components/CMakeLists.txt b/examples/atclient_esp32_static_no_components/CMakeLists.txt new file mode 100644 index 00000000..02baa343 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.19) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(atclient_esp32_static_no_components) \ No newline at end of file diff --git a/examples/atclient_esp32_static_no_components/main/CMakeLists.txt b/examples/atclient_esp32_static_no_components/main/CMakeLists.txt new file mode 100644 index 00000000..5e990671 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/CMakeLists.txt @@ -0,0 +1,11 @@ +idf_component_register( + SRCS "main.c" + INCLUDE_DIRS "atchops/include" "atclient/include" "." + REQUIRES mbedtls +) + +add_prebuilt_library(atchops "${CMAKE_CURRENT_LIST_DIR}/atchops/lib/libatchops.a" REQUIRES mbedtls) +target_link_libraries(${COMPONENT_LIB} INTERFACE atchops) + +add_prebuilt_library(atclient "${CMAKE_CURRENT_LIST_DIR}/atclient/lib/libatclient.a" REQUIRES mbedtls atchops) +target_link_libraries(${COMPONENT_LIB} INTERFACE atclient) \ No newline at end of file diff --git a/examples/atclient_esp32_static_no_components/main/atchops/CMakeLists.txt b/examples/atclient_esp32_static_no_components/main/atchops/CMakeLists.txt new file mode 100644 index 00000000..c8b1c2dd --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atchops/CMakeLists.txt @@ -0,0 +1,7 @@ +# register this directory as a component +idf_component_register() + +add_prebuilt_library(atchops ${CMAKE_CURRENT_LIST_DIR}/lib/libatchops.a REQUIRES mbedtls) + +target_include_directories(atchops INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(${COMPONENT_LIB} INTERFACE atchops) # add it to the component library \ No newline at end of file diff --git a/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/aes_ctr.h b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/aes_ctr.h new file mode 100644 index 00000000..ca9eb607 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/aes_ctr.h @@ -0,0 +1,31 @@ +#pragma once + +typedef enum AESKeySize { + AES_128 = 128, // not tested + AES_192 = 192, // not tested + AES_256 = 256, +} AESKeySize; + +int atchops_aes_ctr_encrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned long *ciphertextbase64olen); + +int atchops_aes_ctr_decrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned long *plaintextolen); diff --git a/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/base64.h b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/base64.h new file mode 100644 index 00000000..d1a9c227 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/base64.h @@ -0,0 +1,25 @@ +#pragma once + +/** + * @brief Base64 encode some bytes + * + * @param src src bytes that you want to encode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 encoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_encode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); + +/** + * @brief Base64 decode some bytes + * + * @param src src bytes that you want to decode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 decoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_decode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); diff --git a/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/rsa.h b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/rsa.h new file mode 100644 index 00000000..bffb64b4 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/rsa.h @@ -0,0 +1,84 @@ +#pragma once + +#include "sha.h" + +typedef struct rsa_param +{ + unsigned long len; // length of the number in bytes + unsigned char *value; // hex byte array of the number +} rsa_param; + +typedef struct atchops_rsa_publickey +{ + rsa_param n; // modulus + rsa_param e; // public exponent +} atchops_rsa_publickey; + +typedef struct atchops_rsa_privatekey +{ + rsa_param n; // modulus + rsa_param e; // public exponent + rsa_param d; // private exponent + rsa_param p; // prime 1 + rsa_param q; // prime 2 +} atchops_rsa_privatekey; + +/** + * @brief Populate a public key struct from a base64 string + * + * @param publickeybase64 a base64 string representing an RSA 2048 Public Key + * @param publickeybase64len the length of the base64 string + * @param publickeystruct the public key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_publickey(const char *publickeybase64, const unsigned long publickeybase64len, atchops_rsa_publickey *publickeystruct); + +/** + * @brief Populate a private key struct from a base64 string + * + * @param privatekeybase64 the base64 string representing an RSA 2048 Private Key + * @param privatekeybase64len the length of the base64 string + * @param privatekeystruct the private key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_privatekey(const char *privatekeybase64, const unsigned long privatekeybase64len, atchops_rsa_privatekey *privatekeystruct); + +/** + * @brief Sign a message with an RSA private key + * + * @param privatekeystruct the private key struct to use for signing, see atchops_rsa_populate_privatekey + * @param mdtype the hash type to use, see atchops_md_type, e.g. ATCHOPS_MD_SHA256 + * @param message the message to sign + * @param messagelen the length of the message, most people use strlen() to find this length + * @param signature the signature buffer to populate + * @param signaturelen the length of the signature buffer + * @param signatureolen the length of the signature buffer after signing + * @return int 0 on success + */ +int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, const unsigned char *message, const unsigned long messagelen, unsigned char *signature, const unsigned long signaturelen, unsigned long *signatureolen); + +/** + * @brief Encrypt bytes with an RSA public key + * + * @param publickeystruct the public key struct to use for encryption, see atchops_rsa_populate_publickey + * @param plaintext the plaintext to encrypt + * @param plaintextlen the length of the plaintext, most people use strlen() to find this length + * @param ciphertext the ciphertext buffer to populate + * @param ciphertextlen the length of the ciphertext buffer + * @param ciphertextolen the length of the ciphertext buffer after encryption + * @return int 0 on success + */ +int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned char *plaintext, const unsigned long plaintextlen, unsigned char *ciphertext, const unsigned long ciphertextlen, unsigned long *ciphertextolen); + +/** + * @brief Decrypt bytes with an RSA private key + * + * @param privatekeystruct the private key struct to use for decryption, see atchops_rsa_populate_privatekey + * @param ciphertextbase64 the ciphertext to decrypt, base64 encoded + * @param ciphertextbase64len the length of the ciphertext, most people use strlen() to find this length + * @param plaintext the plaintext buffer to populate + * @param plaintextlen the length of the plaintext buffer + * @param plaintextolen the length of the plaintext buffer after decryption + * @return int 0 on success + */ +int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, const unsigned long plaintextlen, unsigned long *plaintextolen); diff --git a/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/sha.h b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/sha.h new file mode 100644 index 00000000..786522e5 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atchops/include/atchops/sha.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + ATCHOPS_MD_NONE=0, /**< None. */ + ATCHOPS_MD_MD5, /**< The MD5 message digest. */ + ATCHOPS_MD_SHA1, /**< The SHA-1 message digest. */ + ATCHOPS_MD_SHA224, /**< The SHA-224 message digest. */ + ATCHOPS_MD_SHA256, /**< The SHA-256 message digest. */ + ATCHOPS_MD_SHA384, /**< The SHA-384 message digest. */ + ATCHOPS_MD_SHA512, /**< The SHA-512 message digest. */ + ATCHOPS_MD_RIPEMD160, +} atchops_md_type; + +int atchops_sha_hash(atchops_md_type mdtype, const unsigned char *input, const unsigned long inputlen, unsigned char *output, unsigned long outputlen, unsigned long *outputolen); diff --git a/examples/atclient_esp32_static_no_components/main/atchops/lib/libatchops.a b/examples/atclient_esp32_static_no_components/main/atchops/lib/libatchops.a new file mode 100644 index 0000000000000000000000000000000000000000..c32da5492bf0c27ea82cbe5d99e25d7378dbc1d6 GIT binary patch literal 74662 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 HNW%XBLnG!D literal 0 HcmV?d00001 diff --git a/examples/atclient_esp32_static_no_components/main/atclient/CMakeLists.txt b/examples/atclient_esp32_static_no_components/main/atclient/CMakeLists.txt new file mode 100644 index 00000000..05516682 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atclient/CMakeLists.txt @@ -0,0 +1,7 @@ +# register this directory as a component +idf_component_register() + +add_prebuilt_library(atclient ${CMAKE_CURRENT_LIST_DIR}/lib/libatclient.a REQUIRES mbedtls) + +target_include_directories(atclient INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(${COMPONENT_LIB} INTERFACE atclient) # add it to the component library \ No newline at end of file diff --git a/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/at_logger.h b/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/at_logger.h new file mode 100644 index 00000000..c6be04fc --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/at_logger.h @@ -0,0 +1,9 @@ +#ifndef ATLOGGER_H +#define ATLOGGER_H + +#include + +int atlogger_log(const char *title, const char *message); +int atlogger_logx(const char *title, const unsigned char *bytes, size_t byteslen); + +#endif // ATLOGGER_H diff --git a/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/atkeys_filereader.h b/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/atkeys_filereader.h new file mode 100644 index 00000000..e02fba54 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/atkeys_filereader.h @@ -0,0 +1,59 @@ +#pragma once + +#define TOKEN_AES_PKAM_PUBLIC_KEY "aesPkamPublicKey" +#define TOKEN_AES_PKAM_PUBLIC_KEY_LEN 16 + +#define TOKEN_AES_PKAM_PRIVATE_KEY "aesPkamPrivateKey" +#define TOKEN_AES_PKAM_PRIVATE_KEY_LEN 17 + +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY "aesEncryptPublicKey" +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY_LEN 19 + +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY "aesEncryptPrivateKey" +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY_LEN 20 + +#define TOKEN_SELF_ENCRYPTION_KEY "selfEncryptionKey" +#define TOKEN_SELF_ENCRYPTION_KEY_LEN 17 + +typedef struct atclient_atkeysfile_entry{ + size_t len; + char *key; +} atclient_atkeysfile_entry; + +typedef struct atclient_atkeysfile { + atclient_atkeysfile_entry *aes_pkam_public_key; + atclient_atkeysfile_entry *aes_pkam_private_key; + atclient_atkeysfile_entry *aes_encrypt_public_key; + atclient_atkeysfile_entry *aes_encrypt_private_key; + atclient_atkeysfile_entry *self_encryption_key; +} atclient_atkeysfile; + +void atclient_atkeysfile_init(atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_read(const char *path, atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_write(const char *path, const char *atsign, atclient_atkeysfile *atkeysfile); +void atclient_atkeysfile_free(atclient_atkeysfile *atkeysfile); + +/** + * Usage example + * atclient_atkeysfile atkeysfile; + * atclient_atkeysfile_init(&atkeysfile); + * printf("done init...\n") + * + ret = atclient_atkeysfile_read(path, &atkeysfile); + if (ret != 0) + { + goto exit; + } + + printf("done read...\n"); + printf("aes_pkam_public_key: %s\n", atkeysfile.aes_pkam_public_key->key); + printf("aes_pkam_private_key: %s\n", atkeysfile.aes_pkam_private_key->key); + printf("aes_encrypt_public_key: %s\n", atkeysfile.aes_encrypt_public_key->key); + printf("aes_encrypt_private_key: %s\n", atkeysfile.aes_encrypt_private_key->key); + printf("self_encryption_key: %s\n", atkeysfile.self_encryption_key->key); + + printf("writing...\n"); + + ret = atclient_atkeysfile_write("/Users/jeremytubongbanua/.atsign/temp/@smoothalligator_key.atKeys", ATSIGN, &atkeysfile); + * + */ \ No newline at end of file diff --git a/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/connection.h b/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/connection.h new file mode 100644 index 00000000..4d0d2351 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/atclient/include/atclient/connection.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#define HOST "root.atsign.org" +#define PORT 64 + +// #define HOST "245b44d4-a4bd-5f33-b077-c559f956486a.swarm0001.atsign.zone" +// #define PORT 1722 + +#define ROOT_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" \ + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \ + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" \ + "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" \ + "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" \ + "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" \ + "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" \ + "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" \ + "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" \ + "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" \ + "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" \ + "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" \ + "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" \ + "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" \ + "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" \ + "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" \ + "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" \ + "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" \ + "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" \ + "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" \ + "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" \ + "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" \ + "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" \ + "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" \ + "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" \ + "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" \ + "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" \ + "nLRbwHOoq7hHwg==\n" \ + "-----END CERTIFICATE-----\n" + +typedef struct atclient_connection_ctx { + char *host; // assume null terminated, example: "root.atsign.org" + int port; // example: 64 + char *cert_pem; // assume null terminated, example: "-----BEGIN CERTIFICATE-----\nMIIF..." + void *server_fd; + void *ssl; + void *conf; + void *cacert; + void *entropy; + void *ctr_drbg; + void *saved_session; +} atclient_connection_ctx; + +void atclient_connection_init(atclient_connection_ctx *ctx); +int atclient_connection_connect(atclient_connection_ctx *ctx, const char *host, const int port); +int atclient_connection_send(atclient_connection_ctx *ctx, unsigned char *recv, const size_t recvlen, size_t *olen, const unsigned char *src, const size_t srclen); +void atclient_connection_free(atclient_connection_ctx *ctx); diff --git a/examples/atclient_esp32_static_no_components/main/atclient/lib/libatclient.a b/examples/atclient_esp32_static_no_components/main/atclient/lib/libatclient.a new file mode 100644 index 0000000000000000000000000000000000000000..4cf4208d2c5929e658aff6a4dab133b923101381 GIT binary patch literal 81616 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 literal 0 HcmV?d00001 diff --git a/examples/atclient_esp32_static_no_components/main/main.c b/examples/atclient_esp32_static_no_components/main/main.c new file mode 100644 index 00000000..da808d31 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/main/main.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include "atchops/base64.h" + +void app_main(void) +{ + const char *src = "Lemonade!"; + unsigned long srclen = strlen(src); + + const unsigned long dstlen = 2048; + const unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); + memset(dst, 0, dstlen); + unsigned long dstolen = 0; // written length + + int ret = atchops_base64_encode((const unsigned char *) src, srclen, dst, dstlen, &dstolen); + + printf("atchops_base64_encode: %d\n", ret); + + printf("src: %s\n", src); + printf("dst: %.*s\n", (int) dstolen, dst); + printf("dst bytes: \n"); + for(int i = 0; i < dstolen; i++) + { + printf("%02x ", *(dst + i)); + } + printf("\n"); +} \ No newline at end of file From 3f6760044abc3c7f607758251d68455c5cb78481 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 21 Aug 2023 19:48:54 -0400 Subject: [PATCH 17/26] chore: deleted my shell scripts --- packages/atchops/install-here.sh | 7 ------- packages/atchops/install-system.sh | 5 ----- packages/atchops/run-tests.sh | 9 --------- packages/atclient/install-here.sh | 10 ---------- packages/atclient/install-system.sh | 5 ----- packages/atclient/run-tests.sh | 7 ------- 6 files changed, 43 deletions(-) delete mode 100755 packages/atchops/install-here.sh delete mode 100755 packages/atchops/install-system.sh delete mode 100755 packages/atchops/run-tests.sh delete mode 100755 packages/atclient/install-here.sh delete mode 100755 packages/atclient/install-system.sh delete mode 100755 packages/atclient/run-tests.sh diff --git a/packages/atchops/install-here.sh b/packages/atchops/install-here.sh deleted file mode 100755 index 1c531386..00000000 --- a/packages/atchops/install-here.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -eu -sudo rm -f build/CMakeCache.txt -sudo rm -rf install -mkdir -p install -cmake -S . -B build -DCMAKE_INSTALL_PREFIX=./install -DATCHOPS_FETCH_MBEDTLS=ON -cmake --build build --target install/local \ No newline at end of file diff --git a/packages/atchops/install-system.sh b/packages/atchops/install-system.sh deleted file mode 100755 index e9b6af48..00000000 --- a/packages/atchops/install-system.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -eu -sudo rm -f build/CMakeCache.txt -cmake -S . -B build -DATCHOPS_FETCH_MBEDTLS=ON -sudo cmake --build build --target install \ No newline at end of file diff --git a/packages/atchops/run-tests.sh b/packages/atchops/run-tests.sh deleted file mode 100755 index 8c0e366d..00000000 --- a/packages/atchops/run-tests.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -eu -# sudo rm -rf build -sudo cmake -S . -B build -DATCHOPS_BUILD_TESTS=ON -DATCHOPS_FETCH_MBEDTLS=ON -DCMAKE_INSTALL_PREFIX=./install -sudo rm -rf install -sudo cmake --build build --target all -cd build/tests -sudo ctest --output-on-failure -V -cd ../.. \ No newline at end of file diff --git a/packages/atclient/install-here.sh b/packages/atclient/install-here.sh deleted file mode 100755 index 69a246e4..00000000 --- a/packages/atclient/install-here.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# this script creates a folder called install and will show you what kind of "mess" it makes. BY "mess" I mean what files it will install and where. - -set -eu -sudo rm -f build/CMakeCache.txt -sudo rm -rf install -mkdir -p install -cmake -S . -B build -DCMAKE_INSTALL_PREFIX=./install -sudo cmake --build build --target install \ No newline at end of file diff --git a/packages/atclient/install-system.sh b/packages/atclient/install-system.sh deleted file mode 100755 index e4a477dd..00000000 --- a/packages/atclient/install-system.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -eu -sudo rm -f build/CMakeCache.txt -cmake -S . -B build -DATCLIENT_FETCH_ATCHOPS=ON -DATCLIENT_FETCH_MBEDTLS=ON -sudo cmake --build build --target install diff --git a/packages/atclient/run-tests.sh b/packages/atclient/run-tests.sh deleted file mode 100755 index 11cd780b..00000000 --- a/packages/atclient/run-tests.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -eu -cmake -S . -B build -DATCLIENT_BUILD_TESTS=ON -DATCLIENT_FETCH_MBEDTLS=OFF -DATCLIENT_FETCH_ATCHOPS=OFF -sudo cmake --build build --target all -cd build/tests -ctest --output-on-failure -cd ../.. \ No newline at end of file From 7b285ccdda98ba06aa0db5a082a4ac7f27003a5e Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 21 Aug 2023 19:49:08 -0400 Subject: [PATCH 18/26] chore: deleted python tooling --- packages/atclient/tool.py | 87 ------------------- packages/atclient/tools/command_handler.py | 77 ---------------- .../tools/desktop_mbedtls_command_handler.py | 66 -------------- .../tools/esp32_arduino_command_handler.py | 55 ------------ .../tools/esp32_espidf_command_handler.py | 58 ------------- packages/atclient/tools/init-espidf.bat | 2 - packages/atclient/tools/init-espidf.sh | 4 - 7 files changed, 349 deletions(-) delete mode 100755 packages/atclient/tool.py delete mode 100644 packages/atclient/tools/command_handler.py delete mode 100644 packages/atclient/tools/desktop_mbedtls_command_handler.py delete mode 100644 packages/atclient/tools/esp32_arduino_command_handler.py delete mode 100644 packages/atclient/tools/esp32_espidf_command_handler.py delete mode 100755 packages/atclient/tools/init-espidf.bat delete mode 100755 packages/atclient/tools/init-espidf.sh diff --git a/packages/atclient/tool.py b/packages/atclient/tool.py deleted file mode 100755 index 4ee16d6a..00000000 --- a/packages/atclient/tool.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python - -from argparse import ArgumentParser -from os import path - -root_dir = path.dirname(path.realpath(__file__)) - -### Platform Options ### -available_platforms = ['desktop', 'esp32'] - -available_frameworks = { - 'desktop': ['mbedtls'], - 'esp32': ['espidf', 'arduino'] -} - -base_commands = ['init', 'build', 'clean', 'project']#, 'test'] - -available_commands = { - 'desktop': { - 'mbedtls': base_commands + ['test'], - }, - 'esp32': { - 'espidf': base_commands, # + ['flash', 'monitor'] - 'arduino': base_commands, # + ['flash', 'monitor'] - }, -} - -### Argument Parsing ### - -class ArgNamespace(object): pass - -def get_platform_parser(parser, namespace): - parser.add_argument('-p','--platform', choices=available_platforms, default='desktop') - parser.parse_known_args(namespace=namespace) - return parser, namespace - -def get_framework_parser(parser, namespace): - choices = available_frameworks[namespace.platform] - parser.add_argument('-f','--framework', choices=choices, default=choices[0]) - parser.parse_known_args(namespace=namespace) - return parser, namespace - -def get_command_parser(parser, namespace): - choices = available_commands[namespace.platform][namespace.framework] - parser.add_argument('command', choices=choices) - parser.parse_known_args(namespace=namespace) - return parser, namespace - -def get_additional_args(parser, namespace): - if namespace.command == 'project': - parser.add_argument('project_path', help='Path to output directory') - if namespace.command == 'build': - parser.add_argument('-c','--clean', action='store_true', help='Clean build directory before building') - parser.add_argument('-o','--output', help='Output library file path') - parser.parse_args(namespace=namespace) - return parser, namespace - -def parse_args(): - parser = ArgumentParser(conflict_handler='resolve') - namespace = ArgNamespace() - parser, namespace = get_platform_parser(parser, namespace) - parser, namespace = get_framework_parser(parser, namespace) - parser, namespace = get_command_parser(parser, namespace) - parser, namespace = get_additional_args(parser, namespace) - return namespace.platform, namespace.framework, namespace.command, namespace - -def main(): - platform, framework, command, args = parse_args() - if platform == 'desktop': - if framework == 'mbedtls': - from tools.desktop_mbedtls_command_handler import DesktopMbedTLSCommandHandler - handler = DesktopMbedTLSCommandHandler(root_dir) - else: raise Exception('Unknown framework: ' + framework) - elif platform == 'esp32': - if framework == 'espidf': - from tools.esp32_espidf_command_handler import ESP32ESPIDFCommandHandler - handler = ESP32ESPIDFCommandHandler(root_dir) - elif framework == 'arduino': - from tools.esp32_arduino_command_handler import ESP32ArduinoCommandHandler - handler = ESP32ArduinoCommandHandler(root_dir) - else: raise Exception('Unknown framework: ' + framework) - else: raise Exception('Unknown platform: ' + platform) - handler.handle(command, args) - - -if __name__ == '__main__': - main() diff --git a/packages/atclient/tools/command_handler.py b/packages/atclient/tools/command_handler.py deleted file mode 100644 index c67e89a9..00000000 --- a/packages/atclient/tools/command_handler.py +++ /dev/null @@ -1,77 +0,0 @@ -class CommandHandler(object): - def __init__(self, platform, framework, root_dir): - self.platform = platform - self.framework = framework - self.dir_name = platform + '_' + framework - self.root_dir = root_dir+'/..' - self.build_dir=self.root_dir+'/build/'+self.dir_name - pass - def handle(self, command, args): - if command == 'init': self.init(args) - elif command == 'build': self.build(args) - elif command == 'clean': self.clean(args) - elif command == 'project': self.project(args) - elif command == 'test': self.test(args) - else: raise Exception('Unknown command: ' + command) - pass - def init(self, args): - raise Exception('Not implemented') - pass - def build(self, args): - if args.clean: self.clean(args) - pass - def _build_copy(self, args, extra_lib_dirs=[]): - from glob import glob - from shutil import copy - # Copy additional libraries to lib directory - for extra_lib_dir in extra_lib_dirs: - for lib_dir in glob(extra_lib_dir+'/lib*.a'): - copy(lib_dir, self.root_dir+'/lib/'+self.dir_name) - # Check if output directory is specified - if args.output is None: return - from os import path, makedirs - output_dir = path.relpath(args.output) - makedirs(output_dir, exist_ok=True) - # Copy libraries to output directory - for lib in glob(self.root_dir+'/lib/'+self.dir_name+'/lib*.a'): - copy(lib, args.output) - print('Library files copied to ' + output_dir) - pass - def _build_fail(self, args): - print('Unable to build project. Please run "tool.py -p {} -f {} clean" and try again.'.format(self.platform, self.framework)) - return - def clean(self, args): - from shutil import rmtree - rmtree(self.root_dir+'/build/'+self.dir_name, ignore_errors=True) - rmtree(self.root_dir+'/lib/'+self.dir_name, ignore_errors=True) - rmtree(self.root_dir+'/test/'+self.dir_name, ignore_errors=True) - print('Done cleaning!') - pass - def project(self, args): - from distutils.dir_util import copy_tree - from os import path - project_path = args.project_path - project_name = path.basename(project_path) - print('Creating project '+project_name+'...') - copy_tree(self.root_dir+'/archetypes/'+self.dir_name, project_path) - return project_path, project_name - def test(self, args): - from subprocess import run - from glob import glob - from os.path import basename - count_failed = 0 - print('\nRunning tests...') - for test in glob(self.root_dir+'/test/'+self.dir_name+'/test_*'): - res = run([test]) - if res.returncode == 0: - print(basename(test) +' passed!') - else: - count_failed += 1 - print(basename(test) +' exited with code '+str(res.returncode)) - print() - if count_failed == 0: - print('All tests passed!') - elif count_failed == 1: - print(str(count_failed)+' test failed!') - else: - print(str(count_failed)+' tests failed!') diff --git a/packages/atclient/tools/desktop_mbedtls_command_handler.py b/packages/atclient/tools/desktop_mbedtls_command_handler.py deleted file mode 100644 index f6360def..00000000 --- a/packages/atclient/tools/desktop_mbedtls_command_handler.py +++ /dev/null @@ -1,66 +0,0 @@ -from command_handler import CommandHandler -class DesktopMbedTLSCommandHandler(CommandHandler): - def __init__(self, root_dir): - super().__init__('desktop', 'mbedtls', root_dir) - pass - def handle(self, command, args): - return super().handle(command, args) - def init(self, args): - from sys import executable - from subprocess import check_call - exit_code = check_call([executable, '-m', 'pip', 'install', 'cmake']) - if exit_code != 0: - print('Unable to automatically install cmake. Please install it manually:') - print('python -m pip install cmake') - pass - def build(self, args): - super().build(args) - from subprocess import check_call - # Run cmake - exit_code = check_call([ - 'cmake', - '-S', self.root_dir, - '-B', self.build_dir, - '-D', 'BUILD_MBEDTLS=ON', - ]) - if exit_code != 0: - super()._build_fail(args) - return - # Run make - exit_code = check_call(['make', '--directory', self.build_dir, 'all']) - if exit_code != 0: - super()._build_fail(args) - return - # Create directories - from os import makedirs - makedirs(self.build_dir+'/lib/', exist_ok=True) - makedirs(self.build_dir+'/test/', exist_ok=True) - # Copy libraries to lib directory and tests to test directory - from glob import glob - from shutil import copy - for lib in glob(self.build_dir+'/lib*.a'): - copy(lib, self.build_dir+'/lib/') - for test in glob(self.build_dir+'/test_*'): - copy(test, self.build_dir+'/test/') - for lib in glob(self.build_dir+'/deps/mbedtls/library/lib*.a'): - copy(lib, self.build_dir+'/lib/') - if args.output is not None: - from os import path - output_dir = path.relpath(args.output) - makedirs(output_dir, exist_ok=True) - for lib in glob(self.build_dir+'/lib/*'): - copy(lib, output_dir) - print('Build successful!') - pass - def clean(self, args): - from shutil import rmtree - rmtree(self.build_dir, ignore_errors=True) - print('Done cleaning: ' + self.build_dir) - pass - def project(self, args): - project_path, project_name = super().project(args) - print('Created project '+project_name) - pass - def test(self, args): - print('Not implemented') - pass diff --git a/packages/atclient/tools/esp32_arduino_command_handler.py b/packages/atclient/tools/esp32_arduino_command_handler.py deleted file mode 100644 index 39b60975..00000000 --- a/packages/atclient/tools/esp32_arduino_command_handler.py +++ /dev/null @@ -1,55 +0,0 @@ -from command_handler import CommandHandler - -class ESP32ArduinoCommandHandler(CommandHandler): - def __init__(self, root_dir): - super().__init__('esp32', 'arduino', root_dir) - from sys import path as python_path - idf_dir = root_dir+'/deps/arduino-esp32' - python_path.append(idf_dir+'/tools') - pass - def handle(self, command, args): - return super().handle(command, args) - def init(self, args): - from sys import executable - from subprocess import check_call - exit_code = check_call([executable, '-m', 'pip', 'install', 'cmake==3.25.2']) - if exit_code != 0: - print('Unable to automatically install cmake. Please install it manually:') - print('python -m pip install cmake==3.25.2') - pass - pass - def build(self, args): - super().build(args) - from subprocess import check_call - # Run cmake - exit_code = check_call([ - 'cmake', - '-S', self.root_dir, - '-B', self.root_dir+'/build/'+self.dir_name, - ' -D', 'BUILD_ARDUINO=ON', - ]) - if exit_code != 0: - super()._build_fail(args) - return - # Run make - exit_code = check_call(['make', '-C', self.root_dir+'/build/'+self.dir_name, 'all']) - if exit_code != 0: - super()._build_fail(args) - return - # Create lib directory - from os import makedirs - makedirs(self.root_dir+'/lib/'+self.dir_name, exist_ok=True) - # Copy libraries to lib directory - from glob import glob - from shutil import copy - for lib in glob(self.root_dir+'/build/'+self.dir_name+'/lib*.a'): - copy(lib, self.root_dir+'/lib/'+self.dir_name+'/') - super()._build_copy(args) - print('Build successful!') - pass - def clean(self, args): - return super().clean(args) - def project(self, args): - project_path, project_name = super().project(args) - print('Created project '+project_name) - pass diff --git a/packages/atclient/tools/esp32_espidf_command_handler.py b/packages/atclient/tools/esp32_espidf_command_handler.py deleted file mode 100644 index 8f97ea5d..00000000 --- a/packages/atclient/tools/esp32_espidf_command_handler.py +++ /dev/null @@ -1,58 +0,0 @@ -from command_handler import CommandHandler - -class ESP32ESPIDFCommandHandler(CommandHandler): - def __init__(self, root_dir): - super().__init__('esp32', 'espidf', root_dir) - from sys import path as python_path - idf_dir = root_dir+'/deps/esp-idf' - python_path.append(idf_dir+'/tools') - pass - def handle(self, command, args): - return super().handle(command, args) - def init(self, args): - import idf_tools - idf_tools.main(['install']) - idf_tools.main(['install-python-env']) - from platform import system - print('\nPlease run the following command to complete setup:') - if system() == 'Windows': - print('call '+self.root_dir+'/tools/init-espidf.bat') - else: - print('. '+self.root_dir+'/tools/init-espidf.sh') - pass - def build(self, args): - super().build(args) - from os import getenv - if getenv('IDF_PATH') is None: - print('IDF_PATH not set. Please run "./tool.py -p esp32 -f espidf init" first.') - exit(1) - from subprocess import check_call - # Run cmake - exit_code = check_call([ - 'cmake', - '-S', self.root_dir, - '-B', self.root_dir+'/build/'+self.dir_name, - '-D', 'BUILD_ESP_IDF=ON', - ]) - if exit_code != 0: - super()._build_fail(args) - return - # Run make - exit_code = check_call(['make', '-C', self.root_dir+'/build/'+self.dir_name, 'all']) - if exit_code != 0: - super()._build_fail(args) - return - self._build_copy(args) - print('Build successful!') - pass - def clean(self, args): - return super().clean(args) - def project(self, args): - project_path, project_name = super().project(args) - with open(project_path+'/CMakeLists.txt', 'r') as f: - data = f.read() - data = data.replace('', project_name) - with open(project_path+'/CMakeLists.txt', 'w') as f: - f.write(data) - print('Created project '+project_name) - pass diff --git a/packages/atclient/tools/init-espidf.bat b/packages/atclient/tools/init-espidf.bat deleted file mode 100755 index 856bc020..00000000 --- a/packages/atclient/tools/init-espidf.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -call ..\deps\esp-idf\export.bat \ No newline at end of file diff --git a/packages/atclient/tools/init-espidf.sh b/packages/atclient/tools/init-espidf.sh deleted file mode 100755 index 2523b7f5..00000000 --- a/packages/atclient/tools/init-espidf.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh - -IDF_EXPORT_QUIET=1 -. "$(dirname $0)/../deps/esp-idf/export.sh" \ No newline at end of file From 14d63b35cecc8710cf77885e70549af3952d861b Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 21 Aug 2023 19:49:30 -0400 Subject: [PATCH 19/26] docs: filereader not a test --- packages/atclient/tests/example_atkeysfilereader.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/atclient/tests/example_atkeysfilereader.c b/packages/atclient/tests/example_atkeysfilereader.c index f20b172b..c35739a2 100644 --- a/packages/atclient/tests/example_atkeysfilereader.c +++ b/packages/atclient/tests/example_atkeysfilereader.c @@ -1,3 +1,5 @@ +// This file is not captured by the tests + #include #include #include From 602d4a9b4ffd029295e906a75f9956baef78a044 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 21 Aug 2023 19:49:36 -0400 Subject: [PATCH 20/26] docs: packages/atclient/README --- packages/atclient/README.md | 187 +++++++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 48 deletions(-) diff --git a/packages/atclient/README.md b/packages/atclient/README.md index 9e410a5c..c67a3b60 100644 --- a/packages/atclient/README.md +++ b/packages/atclient/README.md @@ -1,85 +1,176 @@ +# atclient -## Project structure +atclient is the core dependency for anything Atsign technology related. atclient depends on [atchops](../atchops/README.md) and [MbedTLS](https://github.com/Mbed-TLS/mbedtls). -`archetypes`: contains useful templates for compiling applications for this library. -`build`: cmake builds to this folder. -`deps`: git submodules and other dependencies. -`example`: example applications. -`include`: declarations (header files). -`src`: definitions (source code). -`lib`: staticly linked libraries. -`targets`: additional configuration for build targets. -`test`: contains the test source code files and where test binaries are moved to. -`tools`: contains tools and scripts, some used by tool.py +This client SDK implements the atProtocol. It is written in C and is intended to be used in embedded systems. This package and the following documentation will assist you in building and using the C SDK for desktop. To use this SDK in something like an ESP32, checkout [atclient_espidf](../atclient_espidf/README.md). -## Dependencies +It is not mandatory to build [atchops](../atchops/README.md) or [MbedTLS](https://github.com/Mbed-TLS/mbedtls) from source to use this package. However, you have the option of doing so if you want faster building/debugging times. You can avoid fetching these packages everytime by setting the `ATCLIENT_FETCH_MBEDTLS` and `ATCLIENT_FETCH_ATCHOPS` to off during the configure step. These steps are illustrated in the [Installing on Linux/MacOS](#installing-on-linuxmacos) section. -- [CMake](https://cmake.org/) (3.13+) -- [Python](https://www.python.org/) (3.6+) -- [Git](https://git-scm.com/) + -## Tools +- [atclient](#atclient) + * [Building Source](#building-source) + + [Installing on Linux/MacOS](#installing-on-linuxmacos) + + [Installing on Windows](#installing-on-windows) + * [Running Tests on Linux/MacOS](#running-tests-on-linuxmacos) + * [Contributing](#contributing) + + [Creating Tests](#creating-tests) + + [Adding New Source Files](#adding-new-source-files) + + [Adding New Include Headers](#adding-new-include-headers) -The entire build pipeline can be run via python using `./tool.py`. -You will need to first update the git submodules: +## Building Source + +To build the source code you will need to have [CMake](https://cmake.org/) installed and a generator like [Ninja](https://ninja-build.org/) or [Unix Makefiles](https://cmake.org/cmake/help/latest/generator/Unix%20Makefiles.html) (which is installed by default on most Linux distros). + +### Installing on Linux/MacOS + +1. Get ahold of the source code either via git clone or from downloading the source from our releases: ```sh -git submodule update --init --recursive +git clone https://github.com/atsign-foundation/at_c.git +cd at_c/packages/atclient ``` -You may also need to install the python dependencies: +2. CMake configure ```sh -python3 -m pip install -r requirements.txt +cmake -S . -B build ``` -Usage: +Alternatively, if you have installed MbedTLS and/or AtChops from source already, you can avoid fetching it everytime with `ATCLIENT_FETCH_MBEDTLS=OFF` and `ATCLIENT_FETCH_ATCHOPS=OFF` respectively. Doing this drastically reduces the time it takes to configure the project: ```sh -usage: tool.py [-h] [-p {desktop,esp32}] [-f {mbedtls}] {init,build,clean,project} +cmake -S . -B build -DATCLIENT_FETCH_MBEDTLS=OFF -DATCLIENT_FETCH_ATCHOPS=OFF ``` +If you would not like the static libraries and include header files to be installed on your system directly, you can specify a custom install directory with `-DCMAKE_INSTALL_PREFIX=/path/to/install`: + Example: -Build for Desktop using MbedTLS: +```sh +cmake -S . -B build -DCMAKE_INSTALL_PREFIX=./install +``` + +The command above will install the static libraries and include header files in the `install` directory in the root of the project. Installing without the `-DCMAKE_INSTALL_PREFIX=./install` flag in the configure step will install the static libraries, include headers, and any binaries in your system directories, such as `/usr/local/lib` and `/usr/local/include`. + +Example of the install directory structure: + +```bash +. +└── install/ + ├── bin + ├── include/ + │ ├── atclient/ + │ │ └── *.h + │ ├── atchops/ + │ │ └── *.h + │ └── mbedtls/ + │ └── *.h + └── lib/ + ├── cmake/ + │ ├── atclient/ + │ │ └── atclient-config.cmake + │ └── atchops/ + │ └── atchops-config.cmake + ├── libatchops.a + ├── libatclient.a + ├── libmbedtls.a + ├── libmbedcrypto.a + └── libmbedx509.a +``` + +3. Install. ```sh -python3 tool.py -p desktop -f mbedtls build +cmake --build build --target install ``` -## Adding your own Test +This is the same as doing `cd build && make install` if you are using something like Unix Makefiles as your generator. -1. Create your test in `test/`. Example: `test/my_test.c` +You may need to use `sudo` depending on your system. -2. Write your test in main(). Example: +4. Building the source code will allow you to use the `atclient` library in your own CMake projects: + +```cmake +find_package(atclient REQUIRED CONFIG) +target_link_libraries(myproj PRIVATE atclient::atclient) +``` -my_test.c +### Installing on Windows -```c -int main() -{ - if(1 == 1) { - return 0; // exit code 0 - } else { - return 1; // exit code 1 - } -} +Coming Soon! + +For now, here are some experimental commands that may work: + +``` +cmake -S . -B build -DATCLIENT_BUILD_TESTS=ON -DATCHOPS_BUILD_TESTS=ON +cmake --build build --config Debug ``` -3. Add your test to `CMakeLists.txt`. Example: +You may also specify a generator in the configure step with something like: `-G "MinGW Makefiles"` -```cmake -add_executable(my_test test/my_test.c) -target_link_libraries(my_test PRIVATE at_client) -add_test( - NAME MY_TEST - COMMAND $) +## Running Tests on Linux/MacOS + +1. Get ahold of the source code either via git clone or from downloading the source from our releases: + +```sh +git clone https://github.com/atsign-foundation/at_c.git +cd at_c/packages/atclient +``` + +2. CMake configure with `-DATCLIENT_BUILD_TESTS=ON` + +```sh +cmake -S . -B build -DATCLIENT_BUILD_TESTS=ON ``` -4. Build and test +3. Build (target is all by default) ```sh -python3 tool.py -p -f build -python3 tool.py -p -f test +cmake --build build ``` + +This is the same as doing `cd build && make all`, if you are using something like Unix Makefiles as your generator. + +4. Run tests + +```sh +cd build/tests && ctest -V --output-on-failure --timeout 10 +``` + +`--timeout 10` times out tests after 10 seconds + +`-V` will output any stdout lines from the tests. + +## Contributing + +When creating source files, include headers, or tests to certain packages, please follow the documentation in their according README files. + +### Creating Tests + +If you want to add a test in atclient, simply add a `test_*.c` file in the `tests` directory. CMake will automatically detect it and add it to the test suite. Ensure that the test file is named `test_*.c` or else it will not be detected. + +Ensure the file has a `int main(int argc, char **argv)` function and returns 0 on success and not 0 on failure. + +### Adding New Source Files + +This one is a little more tricky. Adding a new source file to the project requires a few steps: + +Add the source file to the `CMakeLists.txt` file in the `src` directory. This is so that CMake knows to compile the file. + +Example: + +```cmake +target_sources(atclient PRIVATE + ... + ${CMAKE_CURRENT_LIST_DIR}/src/folder/new_file.c + ... +) +``` + +### Adding New Include Headers + +Simply add the header inside of the `include/` directory. CMake will automatically detect it and add it to the include path. + +If it is added in a subdirectory (like `include/atclient/`), then the include path will be `atclient/` (e.g. `#include `) \ No newline at end of file From 17c47ed02ede716bd974c5dc9b2bcff54fe5e594 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 21 Aug 2023 19:49:53 -0400 Subject: [PATCH 21/26] feat: packages/atclient_espidf build package --- packages/atclient_espidf/CMakeLists.txt | 10 + packages/atclient_espidf/README.md | 201 ++++++++++++++++++ .../atclient_espidf/include/atchops/aes_ctr.h | 31 +++ .../atclient_espidf/include/atchops/base64.h | 25 +++ .../atclient_espidf/include/atchops/rsa.h | 84 ++++++++ .../atclient_espidf/include/atchops/sha.h | 14 ++ .../include/atclient/at_logger.h | 9 + .../include/atclient/atkeys_filereader.h | 59 +++++ .../include/atclient/connection.h | 59 +++++ packages/atclient_espidf/lib/libatchops.a | Bin 0 -> 74666 bytes packages/atclient_espidf/lib/libatclient.a | Bin 0 -> 81616 bytes packages/atclient_espidf/main/CMakeLists.txt | 3 + packages/atclient_espidf/main/main.c | 4 + 13 files changed, 499 insertions(+) create mode 100644 packages/atclient_espidf/CMakeLists.txt create mode 100644 packages/atclient_espidf/README.md create mode 100644 packages/atclient_espidf/include/atchops/aes_ctr.h create mode 100644 packages/atclient_espidf/include/atchops/base64.h create mode 100644 packages/atclient_espidf/include/atchops/rsa.h create mode 100644 packages/atclient_espidf/include/atchops/sha.h create mode 100644 packages/atclient_espidf/include/atclient/at_logger.h create mode 100644 packages/atclient_espidf/include/atclient/atkeys_filereader.h create mode 100644 packages/atclient_espidf/include/atclient/connection.h create mode 100644 packages/atclient_espidf/lib/libatchops.a create mode 100644 packages/atclient_espidf/lib/libatclient.a create mode 100644 packages/atclient_espidf/main/CMakeLists.txt create mode 100644 packages/atclient_espidf/main/main.c diff --git a/packages/atclient_espidf/CMakeLists.txt b/packages/atclient_espidf/CMakeLists.txt new file mode 100644 index 00000000..9846ba21 --- /dev/null +++ b/packages/atclient_espidf/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.19) + +set(EXTRA_COMPONENT_DIRS + ${CMAKE_CURRENT_LIST_DIR}/../atchops + ${CMAKE_CURRENT_LIST_DIR}/../atclient +) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(atclient_espidf) \ No newline at end of file diff --git a/packages/atclient_espidf/README.md b/packages/atclient_espidf/README.md new file mode 100644 index 00000000..32f42aa3 --- /dev/null +++ b/packages/atclient_espidf/README.md @@ -0,0 +1,201 @@ +# atclient_espidf + +Use this package to build atclient static libraries & include headers for your ESP-IDF projects. + + + +- [atclient_espidf](#atclient_espidf) + - [Requirements](#requirements) + - [Consuming AtClient in your ESP-IDF Project](#consuming-atclient-in-your-esp-idf-project) + - [Source Code](#source-code) + - [Static Libraries](#static-libraries) + - [1. Build Static Libraries through Source Code](#1-build-static-libraries-through-source-code) + - [2. Use Static Libraries as a Prebuilt Library](#2-use-static-libraries-as-a-prebuilt-library) + +## Requirements + +- [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) - you will need the `idf.py` tool when building the static libraries +- [Git](https://git-scm.com/) - to clone this repository + +## Consuming AtClient in your ESP-IDF Project + +There are two ways to consume atclient in your ESP-IDF project. + +1. [Source Code](#source-code) - the first way is when you have access to the source code (as a submodule or downloaded source code). + +2. [Static Libraries](#static-libraries) - the second is when you have access to the static libraries and include headers. + +### Source Code + +It is assumed you have access to the source code of this repository whether it is downloaded through our releases or cloned from git (via: `git clone https://github.com/atsign-foundation/at_c.git`) + +1. In your ESP-IDF project, your file structure should look something like this: + +```bash +├── main/ +│ └── CMakeLists.txt +└── CMakeLists.txt +``` + +2. Add atclient and atchops as extra component directories to the root `./CMakeLists.txt`. + +``` +set(EXTRA_COMPONENT_DIRS + /path/to/atclient + /path/to/atchops +) +``` + +Your `./CMakeLists.txt` will look similar to this: + +```cmake +cmake_minimum_required(VERSION 3.19) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(myproj) +``` + +3. Add the `atclient` and `atchops` components as dependencies to your main components's CMakeLists.txt. + +``` +REQUIRES atclient atchops +``` + +Where `main/CMakeLists.txt` may look similar to: + +```cmake +idf_component_register( + SRCS "main.c" + INCLUDE_DIRS "." + REQUIRES atclient atchops mbedtls nvs_flash esp_wifi +) +``` + +### Static Libraries + +#### 1. Build Static Libraries through Source Code + +Building the static libraries will only build atclient and atchops. This will not build MbedTLS which is an underlying dependency for atclient and atchops. + +1. Clone this repository + +```sh +git clone https://github.com/atsign-foundation/at_c +``` + +2. Go into this directory + +```sh +cd at_c/packages/atclient_espidf +``` + +3. get_idf + +```sh +get_idf +``` + +4. Build + +```sh +idf.py build +``` + +5. This will build two directories: `include/` and `lib/` in the root of the project. + +```bash +├── include/ +│ ├── atclient/ +│ │ └── *.h +│ └── atchops/ +│ └── *.h +└── lib/ + ├── libatclient.a + └── libatchops.a +``` + +#### 2. Use Static Libraries as a Prebuilt Library + +You can create ESP-IDF components out of static libraries (which is how we are showing you how to do it), or you can add it to your main component's CMakeLists.txt, which is how [ESP-IDF's example](https://github.com/espressif/esp-idf/tree/master/examples/build_system/cmake/import_prebuilt) shows you how to do it. + +The following steps will show you how to create separate ESP-IDF components (atclient and atchops) and have them each require on MbedTLS. + +1. Create `components/atclient` and `components/atchops` directories in your ESP-IDF project. Create a blank CMakeLists.txt in each directory + +Your project structure may look similar to: + +```bash +├── components/ +│ ├── atclient/ +│ │ └── CMakeLists.txt +│ └── atchops/ +│ └── CMakeLists.txt +├── main/ +│ └── CMakeLists.txt +└── CMakeLists.txt +``` + +2. Add the built static libraries and include headers to their respective component directories. + +Your project structure may look similar to: + +```bash +├── components/ +│ ├── atclient/ +│ │ ├── CMakeLists.txt +│ │ ├── include/atclient/ +│ │ │ └── *.h +│ │ └── lib/ +│ │ └── libatclient.a +│ └── atchops/ +│ ├── CMakeLists.txt +│ ├── include/atchops/ +│ │ └── *.h +│ └── lib/ +│ └── libatchops.a +├── main/ +│ └── CMakeLists.txt +└── CMakeLists.txt +``` + +3. In each CMakeLists.txt, register the component and add the static library to the `${COMPONENT_LIB}`: + +In `components/atclient/CMakeLists.txt`: + +```cmake +idf_component_register() + +add_prebuilt_library(atclient ${CMAKE_CURRENT_LIST_DIR}/lib/libatclient.a + REQUIRES mbedtls +) + +target_include_directories(atclient INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(atclient INTERFACE mbedtls) +``` + +In `components/atchops/CMakeLists.txt`: + +```cmake + +idf_component_register() + +add_prebuilt_library(atchops ${CMAKE_CURRENT_LIST_DIR}/lib/libatchops.a + REQUIRES mbedtls +) + +target_include_directories(atchops INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(atchops INTERFACE mbedtls) +``` + +4. Depend on `atchops` and `atclient` in your main component: + +In `main/CMakeLists.txt`: + +```cmake +idf_component_register( + SRCS "main.c" + INCLUDE_DIRS "." + REQUIRES atclient atchops +) +``` diff --git a/packages/atclient_espidf/include/atchops/aes_ctr.h b/packages/atclient_espidf/include/atchops/aes_ctr.h new file mode 100644 index 00000000..ca9eb607 --- /dev/null +++ b/packages/atclient_espidf/include/atchops/aes_ctr.h @@ -0,0 +1,31 @@ +#pragma once + +typedef enum AESKeySize { + AES_128 = 128, // not tested + AES_192 = 192, // not tested + AES_256 = 256, +} AESKeySize; + +int atchops_aes_ctr_encrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned long *ciphertextbase64olen); + +int atchops_aes_ctr_decrypt( + const char *keybase64, + const unsigned long keybase64len, + const AESKeySize keybits, + const unsigned char *iv, + const unsigned long ivlen, + const unsigned char *ciphertextbase64, + const unsigned long ciphertextbase64len, + unsigned char *plaintext, + const unsigned long plaintextlen, + unsigned long *plaintextolen); diff --git a/packages/atclient_espidf/include/atchops/base64.h b/packages/atclient_espidf/include/atchops/base64.h new file mode 100644 index 00000000..d1a9c227 --- /dev/null +++ b/packages/atclient_espidf/include/atchops/base64.h @@ -0,0 +1,25 @@ +#pragma once + +/** + * @brief Base64 encode some bytes + * + * @param src src bytes that you want to encode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 encoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_encode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); + +/** + * @brief Base64 decode some bytes + * + * @param src src bytes that you want to decode + * @param srclen the length of the src bytes + * @param dst the buffer where the base64 decoded result will be + * @param dstlen the buffer length + * @param writtenlen the length of the result after operation + * @return int 0 on success + */ +int atchops_base64_decode(const unsigned char *src, const unsigned long srclen, unsigned char *dst, unsigned long dstlen, unsigned long *writtenlen); diff --git a/packages/atclient_espidf/include/atchops/rsa.h b/packages/atclient_espidf/include/atchops/rsa.h new file mode 100644 index 00000000..bffb64b4 --- /dev/null +++ b/packages/atclient_espidf/include/atchops/rsa.h @@ -0,0 +1,84 @@ +#pragma once + +#include "sha.h" + +typedef struct rsa_param +{ + unsigned long len; // length of the number in bytes + unsigned char *value; // hex byte array of the number +} rsa_param; + +typedef struct atchops_rsa_publickey +{ + rsa_param n; // modulus + rsa_param e; // public exponent +} atchops_rsa_publickey; + +typedef struct atchops_rsa_privatekey +{ + rsa_param n; // modulus + rsa_param e; // public exponent + rsa_param d; // private exponent + rsa_param p; // prime 1 + rsa_param q; // prime 2 +} atchops_rsa_privatekey; + +/** + * @brief Populate a public key struct from a base64 string + * + * @param publickeybase64 a base64 string representing an RSA 2048 Public Key + * @param publickeybase64len the length of the base64 string + * @param publickeystruct the public key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_publickey(const char *publickeybase64, const unsigned long publickeybase64len, atchops_rsa_publickey *publickeystruct); + +/** + * @brief Populate a private key struct from a base64 string + * + * @param privatekeybase64 the base64 string representing an RSA 2048 Private Key + * @param privatekeybase64len the length of the base64 string + * @param privatekeystruct the private key struct to populate + * @return int 0 on success + */ +int atchops_rsa_populate_privatekey(const char *privatekeybase64, const unsigned long privatekeybase64len, atchops_rsa_privatekey *privatekeystruct); + +/** + * @brief Sign a message with an RSA private key + * + * @param privatekeystruct the private key struct to use for signing, see atchops_rsa_populate_privatekey + * @param mdtype the hash type to use, see atchops_md_type, e.g. ATCHOPS_MD_SHA256 + * @param message the message to sign + * @param messagelen the length of the message, most people use strlen() to find this length + * @param signature the signature buffer to populate + * @param signaturelen the length of the signature buffer + * @param signatureolen the length of the signature buffer after signing + * @return int 0 on success + */ +int atchops_rsa_sign(atchops_rsa_privatekey privatekeystruct, atchops_md_type mdtype, const unsigned char *message, const unsigned long messagelen, unsigned char *signature, const unsigned long signaturelen, unsigned long *signatureolen); + +/** + * @brief Encrypt bytes with an RSA public key + * + * @param publickeystruct the public key struct to use for encryption, see atchops_rsa_populate_publickey + * @param plaintext the plaintext to encrypt + * @param plaintextlen the length of the plaintext, most people use strlen() to find this length + * @param ciphertext the ciphertext buffer to populate + * @param ciphertextlen the length of the ciphertext buffer + * @param ciphertextolen the length of the ciphertext buffer after encryption + * @return int 0 on success + */ +int atchops_rsa_encrypt(atchops_rsa_publickey publickeystruct, const unsigned char *plaintext, const unsigned long plaintextlen, unsigned char *ciphertext, const unsigned long ciphertextlen, unsigned long *ciphertextolen); + +/** + * @brief Decrypt bytes with an RSA private key + * + * @param privatekeystruct the private key struct to use for decryption, see atchops_rsa_populate_privatekey + * @param ciphertextbase64 the ciphertext to decrypt, base64 encoded + * @param ciphertextbase64len the length of the ciphertext, most people use strlen() to find this length + * @param plaintext the plaintext buffer to populate + * @param plaintextlen the length of the plaintext buffer + * @param plaintextolen the length of the plaintext buffer after decryption + * @return int 0 on success + */ +int atchops_rsa_decrypt(atchops_rsa_privatekey privatekeystruct, const unsigned char *ciphertextbase64, const unsigned long ciphertextbase64len, unsigned char *plaintext, const unsigned long plaintextlen, unsigned long *plaintextolen); diff --git a/packages/atclient_espidf/include/atchops/sha.h b/packages/atclient_espidf/include/atchops/sha.h new file mode 100644 index 00000000..786522e5 --- /dev/null +++ b/packages/atclient_espidf/include/atchops/sha.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + ATCHOPS_MD_NONE=0, /**< None. */ + ATCHOPS_MD_MD5, /**< The MD5 message digest. */ + ATCHOPS_MD_SHA1, /**< The SHA-1 message digest. */ + ATCHOPS_MD_SHA224, /**< The SHA-224 message digest. */ + ATCHOPS_MD_SHA256, /**< The SHA-256 message digest. */ + ATCHOPS_MD_SHA384, /**< The SHA-384 message digest. */ + ATCHOPS_MD_SHA512, /**< The SHA-512 message digest. */ + ATCHOPS_MD_RIPEMD160, +} atchops_md_type; + +int atchops_sha_hash(atchops_md_type mdtype, const unsigned char *input, const unsigned long inputlen, unsigned char *output, unsigned long outputlen, unsigned long *outputolen); diff --git a/packages/atclient_espidf/include/atclient/at_logger.h b/packages/atclient_espidf/include/atclient/at_logger.h new file mode 100644 index 00000000..c6be04fc --- /dev/null +++ b/packages/atclient_espidf/include/atclient/at_logger.h @@ -0,0 +1,9 @@ +#ifndef ATLOGGER_H +#define ATLOGGER_H + +#include + +int atlogger_log(const char *title, const char *message); +int atlogger_logx(const char *title, const unsigned char *bytes, size_t byteslen); + +#endif // ATLOGGER_H diff --git a/packages/atclient_espidf/include/atclient/atkeys_filereader.h b/packages/atclient_espidf/include/atclient/atkeys_filereader.h new file mode 100644 index 00000000..e02fba54 --- /dev/null +++ b/packages/atclient_espidf/include/atclient/atkeys_filereader.h @@ -0,0 +1,59 @@ +#pragma once + +#define TOKEN_AES_PKAM_PUBLIC_KEY "aesPkamPublicKey" +#define TOKEN_AES_PKAM_PUBLIC_KEY_LEN 16 + +#define TOKEN_AES_PKAM_PRIVATE_KEY "aesPkamPrivateKey" +#define TOKEN_AES_PKAM_PRIVATE_KEY_LEN 17 + +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY "aesEncryptPublicKey" +#define TOKEN_AES_ENCRYPT_PUBLIC_KEY_LEN 19 + +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY "aesEncryptPrivateKey" +#define TOKEN_AES_ENCRYPT_PRIVATE_KEY_LEN 20 + +#define TOKEN_SELF_ENCRYPTION_KEY "selfEncryptionKey" +#define TOKEN_SELF_ENCRYPTION_KEY_LEN 17 + +typedef struct atclient_atkeysfile_entry{ + size_t len; + char *key; +} atclient_atkeysfile_entry; + +typedef struct atclient_atkeysfile { + atclient_atkeysfile_entry *aes_pkam_public_key; + atclient_atkeysfile_entry *aes_pkam_private_key; + atclient_atkeysfile_entry *aes_encrypt_public_key; + atclient_atkeysfile_entry *aes_encrypt_private_key; + atclient_atkeysfile_entry *self_encryption_key; +} atclient_atkeysfile; + +void atclient_atkeysfile_init(atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_read(const char *path, atclient_atkeysfile *atkeysfile); +int atclient_atkeysfile_write(const char *path, const char *atsign, atclient_atkeysfile *atkeysfile); +void atclient_atkeysfile_free(atclient_atkeysfile *atkeysfile); + +/** + * Usage example + * atclient_atkeysfile atkeysfile; + * atclient_atkeysfile_init(&atkeysfile); + * printf("done init...\n") + * + ret = atclient_atkeysfile_read(path, &atkeysfile); + if (ret != 0) + { + goto exit; + } + + printf("done read...\n"); + printf("aes_pkam_public_key: %s\n", atkeysfile.aes_pkam_public_key->key); + printf("aes_pkam_private_key: %s\n", atkeysfile.aes_pkam_private_key->key); + printf("aes_encrypt_public_key: %s\n", atkeysfile.aes_encrypt_public_key->key); + printf("aes_encrypt_private_key: %s\n", atkeysfile.aes_encrypt_private_key->key); + printf("self_encryption_key: %s\n", atkeysfile.self_encryption_key->key); + + printf("writing...\n"); + + ret = atclient_atkeysfile_write("/Users/jeremytubongbanua/.atsign/temp/@smoothalligator_key.atKeys", ATSIGN, &atkeysfile); + * + */ \ No newline at end of file diff --git a/packages/atclient_espidf/include/atclient/connection.h b/packages/atclient_espidf/include/atclient/connection.h new file mode 100644 index 00000000..4d0d2351 --- /dev/null +++ b/packages/atclient_espidf/include/atclient/connection.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#define HOST "root.atsign.org" +#define PORT 64 + +// #define HOST "245b44d4-a4bd-5f33-b077-c559f956486a.swarm0001.atsign.zone" +// #define PORT 1722 + +#define ROOT_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" \ + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \ + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" \ + "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" \ + "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" \ + "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" \ + "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" \ + "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" \ + "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" \ + "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" \ + "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" \ + "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" \ + "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" \ + "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" \ + "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" \ + "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" \ + "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" \ + "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" \ + "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" \ + "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" \ + "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" \ + "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" \ + "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" \ + "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" \ + "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" \ + "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" \ + "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" \ + "nLRbwHOoq7hHwg==\n" \ + "-----END CERTIFICATE-----\n" + +typedef struct atclient_connection_ctx { + char *host; // assume null terminated, example: "root.atsign.org" + int port; // example: 64 + char *cert_pem; // assume null terminated, example: "-----BEGIN CERTIFICATE-----\nMIIF..." + void *server_fd; + void *ssl; + void *conf; + void *cacert; + void *entropy; + void *ctr_drbg; + void *saved_session; +} atclient_connection_ctx; + +void atclient_connection_init(atclient_connection_ctx *ctx); +int atclient_connection_connect(atclient_connection_ctx *ctx, const char *host, const int port); +int atclient_connection_send(atclient_connection_ctx *ctx, unsigned char *recv, const size_t recvlen, size_t *olen, const unsigned char *src, const size_t srclen); +void atclient_connection_free(atclient_connection_ctx *ctx); diff --git a/packages/atclient_espidf/lib/libatchops.a b/packages/atclient_espidf/lib/libatchops.a new file mode 100644 index 0000000000000000000000000000000000000000..213087df281299dbe14e34dba75bbe0479bbc216 GIT binary patch literal 74666 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 LYqd{y5+U*5sNmvm literal 0 HcmV?d00001 diff --git a/packages/atclient_espidf/lib/libatclient.a b/packages/atclient_espidf/lib/libatclient.a new file mode 100644 index 0000000000000000000000000000000000000000..fcd145791b363bc331958c210cf4ebbf896b760f GIT binary patch literal 81616 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 Date: Tue, 22 Aug 2023 10:37:07 -0400 Subject: [PATCH 22/26] docs: --- README.md | 5 ++++- examples/atclient_esp32_static_no_components/README.md | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 examples/atclient_esp32_static_no_components/README.md diff --git a/README.md b/README.md index ac25df45..765ccfa1 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,13 @@ - [atchops](./packages/atchops/README.md) stands for cryptographic and hashing operations catered for the atProtocol, uses [MbedTLS crypto](https://github.com/Mbed-TLS/mbedtls) as a dependency. - [atclient](./packages/atclient/README.md) is the core dependency for anything Atsign technology related. atclient depends on [atchops](./packages/atchops/README.md) and [MbedTLS](https://github.com/Mbed-TLS/mbedtls) -- [atclient_espidf](./packages/atclient_espidf/README.md) +- [atclient_espidf](./packages/atclient_espidf/README.md) build atclient for ESP-IDF ## Examples +- [atclient_esp32_source](./examples/atclient_esp32_source/README.md) is an example of how to use atclient in your ESP-IDF with the source code. +- [atclient_esp32_static_components](./examples/atclient_esp32_static_components/README.md) is an example of how to use atclient in your ESP-IDF project with static libraries in separated components built from [atclient_espidf](./packages/atclient_espidf/README.md). +- [atclient_esp32_static_no_components](./examples/atclient_esp32_static_no_components/) is an example of how to use atclient in your ESP-IDF project with static libraries without components built from [atclient_espidf](./packages/atclient_espidf/README.md). - [repl](./examples/repl/README.md) is a command line interface for interacting with the atProtocol. Works on Desktop Linux/MacOS. ## Contributing diff --git a/examples/atclient_esp32_static_no_components/README.md b/examples/atclient_esp32_static_no_components/README.md new file mode 100644 index 00000000..8f0fe7f0 --- /dev/null +++ b/examples/atclient_esp32_static_no_components/README.md @@ -0,0 +1,3 @@ +# atclient_esp32_static_no_components + +This example achieves the same outcome as [atclient_esp32_static_components](../atclient_esp32_static_components/README.md) but without the use of components. This is following the same example that ESP-IDf [provides](https://github.com/espressif/esp-idf/tree/master/examples/build_system/cmake/import_prebuilt), but I find components to be more elegant. \ No newline at end of file From fe8b15f9aba5d8f4e1c3fb068a9e0b16f55bbec9 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 22 Aug 2023 10:47:33 -0400 Subject: [PATCH 23/26] docs: --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 765ccfa1..fb55fa20 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Read [CONTRIBUTING.md](./CONTRIBUTING.md) for information on how to properly fork and open a pull request. -When creating source files, include headers, or tests to certain packages, please follow the documentation in their according README files. +When creating source files, include headers, or tests to certain packages, please follow the documentation in their according README files (for example [atclient Contributing](./packages/atclient/README.md)). ## Maintainers From 73794f704c775390d6089b14ae76739a7e188f83 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 22 Aug 2023 10:49:01 -0400 Subject: [PATCH 24/26] chore: fix home dir --- .vscode/c_cpp_properties.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 975b9886..7220efd5 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -7,7 +7,7 @@ "${default}", "${workspaceFolder}/**", "/usr/local/include", - "/Users/jeremytubongbanua/esp/esp-idf/components/**" + "~/esp/esp-idf/components/**" ], "compilerPath": "/usr/bin/gcc", "cStandard": "c99", From 633339750072b71743540442be90a6b7d81a1385 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 22 Aug 2023 12:52:46 -0400 Subject: [PATCH 25/26] feat: sample cmake project --- examples/sample_cmake_project/CMakeLists.txt | 12 +++++++++ examples/sample_cmake_project/main.c | 28 ++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 examples/sample_cmake_project/CMakeLists.txt create mode 100644 examples/sample_cmake_project/main.c diff --git a/examples/sample_cmake_project/CMakeLists.txt b/examples/sample_cmake_project/CMakeLists.txt new file mode 100644 index 00000000..de31f73c --- /dev/null +++ b/examples/sample_cmake_project/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.19) + +project(sample_cmake_project) + +# find_package(atchops REQUIRED CONFIG) +find_package(atclient REQUIRED CONFIG) + +message(STATUS "Found atclient!") + +add_executable(exec ${CMAKE_CURRENT_LIST_DIR}/main.c) + +target_link_libraries(exec PRIVATE atclient::atclient) diff --git a/examples/sample_cmake_project/main.c b/examples/sample_cmake_project/main.c new file mode 100644 index 00000000..0c9fcab7 --- /dev/null +++ b/examples/sample_cmake_project/main.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +int main() +{ + const char *src = "Hello, World!\n"; + const unsigned long srclen = strlen(src); + + const unsigned long dstlen = 2048; + unsigned char *dst = malloc(sizeof(unsigned char) * dstlen); + memset(dst, 0, dstlen); + unsigned long olen = 0; + + int ret = atchops_base64_encode((const unsigned char *) src, srclen, dst, dstlen, &olen); + + printf("atchops_base64_encode: %d\n", ret); + + printf("dst: %s\n", dst); + for(int i = 0; i < olen; i++) + { + printf("%02x ", *(dst + i)); + } + printf("\n"); + + return 0; +} From da9f3b6d98a9d7bb91984ddb17420fbfe0e3b5aa Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 22 Aug 2023 12:52:55 -0400 Subject: [PATCH 26/26] chore: --- examples/sample_cmake_project/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/sample_cmake_project/CMakeLists.txt b/examples/sample_cmake_project/CMakeLists.txt index de31f73c..bd06ca72 100644 --- a/examples/sample_cmake_project/CMakeLists.txt +++ b/examples/sample_cmake_project/CMakeLists.txt @@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.19) project(sample_cmake_project) -# find_package(atchops REQUIRED CONFIG) find_package(atclient REQUIRED CONFIG) message(STATUS "Found atclient!")