From e66e87aa8e627b1561069a51b49859c2bf460190 Mon Sep 17 00:00:00 2001 From: Peter Dunshee Date: Wed, 22 Nov 2017 09:21:59 -0600 Subject: [PATCH] Add Atmel CryptoAuthLib to extras This is Atmel/Microchip's official library for interfacing to the Atmel ATECC508 chip. The submodule points to their repository in GitHub. Additionally, this includes the HAL necessary to use this library in esp_open_rtos using the i2c library in extras/i2c. I have also included a tool I wrote to play with the chip as an example under examples/atcatool. The extras module currently overrides atca_iface.h to fix bug in cryptoauthlib (c11-only feature, which breaks c++ builds that want to use cryptoauthlib) --- .gitmodules | 3 + examples/atcatool/FreeRTOSConfig.h | 12 + examples/atcatool/Makefile | 5 + examples/atcatool/cmd_ecdh.c | 56 +++ examples/atcatool/cmd_priv.c | 91 +++++ examples/atcatool/cmd_pub.c | 59 +++ examples/atcatool/cmd_verify.c | 97 +++++ examples/atcatool/cmdutility.c | 168 ++++++++ examples/atcatool/pubkeyprog.c | 245 ++++++++++++ examples/atcatool/uart_cmds.h | 24 ++ extras/cryptoauthlib/README.md | 6 + extras/cryptoauthlib/atca_iface.h | 173 ++++++++ extras/cryptoauthlib/component.mk | 41 ++ extras/cryptoauthlib/cryptoauthlib | 1 + extras/cryptoauthlib/cryptoauthlib_init.c | 83 ++++ extras/cryptoauthlib/cryptoauthlib_init.h | 15 + extras/cryptoauthlib/defaults.mk | 15 + .../hal/atca_hal_espfreertos_i2c.c | 372 ++++++++++++++++++ 18 files changed, 1466 insertions(+) create mode 100644 examples/atcatool/FreeRTOSConfig.h create mode 100644 examples/atcatool/Makefile create mode 100644 examples/atcatool/cmd_ecdh.c create mode 100644 examples/atcatool/cmd_priv.c create mode 100644 examples/atcatool/cmd_pub.c create mode 100644 examples/atcatool/cmd_verify.c create mode 100644 examples/atcatool/cmdutility.c create mode 100644 examples/atcatool/pubkeyprog.c create mode 100644 examples/atcatool/uart_cmds.h create mode 100644 extras/cryptoauthlib/README.md create mode 100644 extras/cryptoauthlib/atca_iface.h create mode 100644 extras/cryptoauthlib/component.mk create mode 160000 extras/cryptoauthlib/cryptoauthlib create mode 100644 extras/cryptoauthlib/cryptoauthlib_init.c create mode 100644 extras/cryptoauthlib/cryptoauthlib_init.h create mode 100644 extras/cryptoauthlib/defaults.mk create mode 100644 extras/cryptoauthlib/hal/atca_hal_espfreertos_i2c.c diff --git a/.gitmodules b/.gitmodules index 5647056..78ff2cc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,3 +46,6 @@ [submodule "lvgl/lv_examples"] path = lvgl/lv_examples url = https://github.com/littlevgl/lv_examples.git +[submodule "extras/cryptoauthlib/cryptoauthlib"] + path = extras/cryptoauthlib/cryptoauthlib + url = https://github.com/MicrochipTech/cryptoauthlib.git diff --git a/examples/atcatool/FreeRTOSConfig.h b/examples/atcatool/FreeRTOSConfig.h new file mode 100644 index 0000000..0a9a6b7 --- /dev/null +++ b/examples/atcatool/FreeRTOSConfig.h @@ -0,0 +1,12 @@ +/* Terminal FreeRTOSConfig overrides. + + This is intended as an example of overriding some of the default FreeRTOSConfig settings, + which are otherwise found in FreeRTOS/Source/include/FreeRTOSConfig.h +*/ + +/* The serial driver depends on counting semaphores */ +#define configUSE_COUNTING_SEMAPHORES 1 + +/* Use the defaults for everything else */ +#include_next + diff --git a/examples/atcatool/Makefile b/examples/atcatool/Makefile new file mode 100644 index 0000000..d70a5a7 --- /dev/null +++ b/examples/atcatool/Makefile @@ -0,0 +1,5 @@ +PROGRAM=atcatool +EXTRA_COMPONENTS=extras/stdin_uart_interrupt extras/i2c extras/cryptoauthlib +EXTRA_CFLAGS += -DATCAPRINTF +ATEC_PRINTF_ENABLE = 1 +include ../../common.mk diff --git a/examples/atcatool/cmd_ecdh.c b/examples/atcatool/cmd_ecdh.c new file mode 100644 index 0000000..dc9cac4 --- /dev/null +++ b/examples/atcatool/cmd_ecdh.c @@ -0,0 +1,56 @@ + +#include "uart_cmds.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +ATCA_STATUS cmd_ecdh(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status; + if (argc >= 2) { + uint8_t slot_num = atoi(argv[1]); + if (slot_num > 0x7){ + LOG("Invalid slot number %d; must be between 0 and 7 for private keys", slot_num); + return ATCA_BAD_PARAM; + } + + printf("Performing ECDH key exchange in slot %d\n", slot_num); + uint8_t pubkeybytes[100]; + int pubkeylen = sizeof(pubkeybytes); + status = read_pubkey_stdin(pubkeybytes, &pubkeylen); + if (status != ATCA_SUCCESS) + { + RETURN(status, "Failed to read public key"); + } + + LOG("Got valid looking pubkey; does this look correct?"); + uint8_t *keyptr = pubkeybytes + (pubkeylen - ATCA_PUB_KEY_SIZE); + atcab_printbin_label((const uint8_t*)"pubkey ", keyptr, ATCA_PUB_KEY_SIZE); + + if (!prompt_user()) { + printf("Aborting\n"); + }else{ + printf("Performing ECDH key exchange\n"); + } + + // Write key to device + uint8_t pmk[ATCA_PRIV_KEY_SIZE]; + status = atcab_ecdh(slot_num, keyptr, pmk); + if(status != ATCA_SUCCESS){ + RETURN(status, "Failed to obtain pre-master key"); + } + atcab_printbin_label((const uint8_t*)"pmk ", pmk, ATCA_PRIV_KEY_SIZE); + + return ATCA_SUCCESS; + } else { + printf("Error: missing slot number.\n"); + return ATCA_BAD_PARAM; + } +} diff --git a/examples/atcatool/cmd_priv.c b/examples/atcatool/cmd_priv.c new file mode 100644 index 0000000..2b2f18d --- /dev/null +++ b/examples/atcatool/cmd_priv.c @@ -0,0 +1,91 @@ + +#include "uart_cmds.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +// get cert and priv key +//> openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp256r1) -keyout cert.key -out cert.crt -days 3650 + +//#define PEM_PRIV_KEY_OFFSET 5 +#define PEM_PRIV_KEY_OFFSET 7 + +ATCA_STATUS cmd_priv(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status; + if (argc >= 2) { + uint8_t slot_num = atoi(argv[1]); + if (slot_num > 0x7){ + LOG("Invalid slot number %d; must be between 0 and 7 for private keys", slot_num); + return ATCA_BAD_PARAM; + } + + uint8_t write_key_slot = 0; + uint8_t *write_key = NULL; + uint8_t random_num[RANDOM_NUM_SIZE]; + if (argc >= 3) { + write_key_slot = atoi(argv[2]); + if (write_key_slot == slot_num || write_key_slot < 0x0 || write_key_slot > 0xF) { + LOG("Invalid slot for write key (%d); trying unencrypted write", (int)write_key_slot); + write_key_slot = 0; + write_key = NULL; + } else { + LOG("Writing write key to slot %d", (int)write_key_slot); + if((status = atcab_random(random_num)) != ATCA_SUCCESS){ + RETURN(status, "Could not make random number"); + } + write_key = random_num; + if((status = atcab_write_bytes_zone(ATCA_ZONE_DATA, write_key_slot, 0, write_key, RANDOM_NUM_SIZE)) != ATCA_SUCCESS){ + RETURN(status, "Could not commit write key"); + } + } + } + + printf("Programming private key in slot %d\n", slot_num); + uint8_t privkeybytes[200]; + int privkeylen = sizeof(privkeybytes); + status = read_privkey_stdin(privkeybytes, &privkeylen); + if (status != ATCA_SUCCESS) + { + RETURN(status, "Failed to read private key"); + } + + LOG("Got valid looking private key; does this look correct?"); + uint8_t *keyptr = privkeybytes + PEM_PRIV_KEY_OFFSET; + atcab_printbin_label((const uint8_t*)"privkey ", keyptr, ATCA_PRIV_KEY_SIZE); + + if (!prompt_user()) { + printf("Aborting\n"); + return ATCA_SUCCESS; + }else{ + printf("Writing key\n"); + } + + // Write key to device + uint8_t priv_key[ATCA_PRIV_KEY_SIZE + 4] = {0}; + memcpy(priv_key + 4, keyptr, ATCA_PRIV_KEY_SIZE); + status = atcab_priv_write(slot_num, priv_key, write_key_slot, write_key); + if(status != ATCA_SUCCESS){ + RETURN(status, "Failed to write key to slot"); + } + + // Read public key + uint8_t pub_key[ATCA_PUB_KEY_SIZE] = {0}; + if((status = atcab_get_pubkey(slot_num, pub_key)) != ATCA_SUCCESS){ + RETURN(status, "Could not get public key"); + } + atcab_printbin_label((const uint8_t*)"pubkey ", pub_key, sizeof(pub_key)); + + return ATCA_SUCCESS; + } else { + printf("Error: missing slot number.\n"); + return ATCA_BAD_PARAM; + } +} diff --git a/examples/atcatool/cmd_pub.c b/examples/atcatool/cmd_pub.c new file mode 100644 index 0000000..463b2cc --- /dev/null +++ b/examples/atcatool/cmd_pub.c @@ -0,0 +1,59 @@ + +#include "uart_cmds.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +// get cert and priv key +//> openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp256r1) -keyout cert.key -out cert.crt -days 3650 +// get pub key +//> openssl x509 -in cert.crt -pubkey -noout > pubkey.pem + +ATCA_STATUS cmd_pub(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status; + if (argc >= 2) { + uint8_t slot_num = atoi(argv[1]); + if (slot_num < 0x8 || slot_num > 0xF){ + LOG("Invalid slot number %d; must be between 8 and 15 for public keys", slot_num); + return ATCA_BAD_PARAM; + } + + printf("Programming public key in slot %d\n", slot_num); + uint8_t pubkeybytes[100]; + int pubkeylen = sizeof(pubkeybytes); + status = read_pubkey_stdin(pubkeybytes, &pubkeylen); + if (status != ATCA_SUCCESS) + { + RETURN(status, "Failed to read public key"); + } + + LOG("Got valid looking pubkey; does this look correct?"); + uint8_t *keyptr = pubkeybytes + (pubkeylen - ATCA_PUB_KEY_SIZE); + atcab_printbin_label((const uint8_t*)"pubkey ", keyptr, ATCA_PUB_KEY_SIZE); + + if (!prompt_user()) { + printf("Aborting\n"); + }else{ + printf("Writing key\n"); + } + + // Write key to device + status = atcab_write_pubkey(slot_num, keyptr); + if(status != ATCA_SUCCESS){ + RETURN(status, "Failed to write key to slot"); + } + + return ATCA_SUCCESS; + } else { + printf("Error: missing slot number.\n"); + return ATCA_BAD_PARAM; + } +} diff --git a/examples/atcatool/cmd_verify.c b/examples/atcatool/cmd_verify.c new file mode 100644 index 0000000..597b66d --- /dev/null +++ b/examples/atcatool/cmd_verify.c @@ -0,0 +1,97 @@ + +#include "uart_cmds.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +// make test hash +//> openssl sha -sha256 cmd_pub.c +// sign test hash +//> openssl sha -sha256 -sign cert.key -hex cmd_pub.c + +void get_line(char *buf, size_t buflen) +{ + char ch; + char cmd[200]; + int i = 0; + while(1) { + if (read(0, (void*)&ch, 1)) { // 0 is stdin + printf("%c", ch); + if (ch == '\n' || ch == '\r') { + cmd[i] = 0; + i = 0; + printf("\n"); + strcpy(buf, cmd); + return; + } else { + if (i < sizeof(cmd)) cmd[i++] = ch; + } + } + } +} + +ATCA_STATUS cmd_verify(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status; + if (argc >= 2) { + uint8_t slot_num = atoi(argv[1]); + if (slot_num < 0x8 || slot_num > 0xF){ + LOG("Invalid slot number %d; must be between 8 and 15 for public keys", slot_num); + return ATCA_BAD_PARAM; + } + + printf("Verifying with public key in slot %d\n", slot_num); + char hash[100]; uint8_t hashbin[100]; int hashlen = sizeof(hashbin); + char sig[200]; uint8_t sigbin[200]; int siglen = sizeof(sigbin); + + printf("Please paste hash in the terminal (hex format)\n"); + get_line(hash, sizeof(hash)); + + status = atcab_hex2bin(hash, strlen(hash), hashbin, &hashlen); + if(status != ATCA_SUCCESS){ + RETURN(status, "Could not parse hash hex"); + } + + printf("Please paste signature in the terminal (hex format)\n"); + get_line(sig, sizeof(sig)); + + status = atcab_hex2bin(sig, strlen(sig), sigbin, &siglen); + if(status != ATCA_SUCCESS){ + RETURN(status, "Could not parse signature hex"); + } + + bool isVerified = false; + printf("Trying to verify with\n"); + atcab_printbin_label((const uint8_t*)"hash ", hashbin, hashlen); + printf("Len %d\n", hashlen); + atcab_printbin_label((const uint8_t*)"sig ", sigbin, siglen); + printf("Len %d\n", siglen); + + uint8_t atca_sig[ATCA_SIG_SIZE] = {0}; + if(!parse_asn1_signature(sigbin, siglen, atca_sig)) + { + RETURN(ATCA_PARSE_ERROR, "Could not parse ASN.1 signature"); + } + + atcab_printbin_label((const uint8_t*)"atca_sig ", atca_sig, ATCA_SIG_SIZE); + printf("Len %d\n", ATCA_SIG_SIZE); + + status = atcab_verify_stored(hashbin, atca_sig, slot_num, &isVerified); + if(status != ATCA_SUCCESS){ + RETURN(status, "Could not verify signature"); + } + + printf(isVerified ? "Signature is valid\n" : "Signature is invalid\n"); + RETURN(status, "Done"); + } else { + printf("Error: missing slot number.\n"); + return ATCA_BAD_PARAM; + } +} diff --git a/examples/atcatool/cmdutility.c b/examples/atcatool/cmdutility.c new file mode 100644 index 0000000..a47eba8 --- /dev/null +++ b/examples/atcatool/cmdutility.c @@ -0,0 +1,168 @@ + +#include "uart_cmds.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +#define RETURN_BOOL(result, message) {printf(": " message "\r\n"); return result;} + +ATCA_STATUS read_key_stdin(const char* beginConst, const char* endConst, uint8_t *keyOut, int *len) +{ + printf("Please paste key in the terminal (PEM format)\n"); + + char ch; + char cmd[81]; + int i = 0; + bool done = false; + uint8_t keybuf[1000] = {0}; // max buffer we should need + int buf_used = 0; + while (!done && buf_used < (sizeof(keybuf) - 1)) + { + if (read(0, (void *)&ch, 1)) + { // 0 is stdin + printf("%c", ch); + if (ch == '\n' || ch == '\r') + { + cmd[i] = 0; + printf("\n"); + // check for end of public key + if (!strcmp(endConst, cmd)) + { + done = true; + printf("Got end of public key\n"); + } + else if (strcmp(beginConst, cmd)) + { + // only copy non-marker text + strcat((char *)keybuf, cmd); + buf_used += i; + } + i = 0; + } + else + { + if (i < sizeof(cmd)) + cmd[i++] = ch; + } + } + } + + // Try to parse + uint8_t keybytes[(sizeof(keybuf) * 3) / 4]; + size_t arrayLen = sizeof(keybytes); + ATCA_STATUS status = atcab_base64decode((const char *)keybuf, sizeof(keybuf), keybytes, &arrayLen); + if(status != ATCA_SUCCESS) + { + RETURN(status, "Error decoding base64 public key"); + } + else if (arrayLen > *len) + { + LOG("Key was the wrong size (%d); expected at most %d", arrayLen, *len); + atcab_printbin_label((const uint8_t*)"decoded ", keybytes, arrayLen); + return ATCA_BAD_PARAM; + } + + memcpy(keyOut, keybytes, arrayLen); + *len = arrayLen; + return ATCA_SUCCESS; +} + +ATCA_STATUS read_pubkey_stdin(uint8_t *keyOut, int *len) +{ + return read_key_stdin(BEGIN_PUB_KEY_CONST, END_PUB_KEY_CONST, keyOut, len); +} + +ATCA_STATUS read_privkey_stdin(uint8_t *keyOut, int *len) +{ + return read_key_stdin(BEGIN_PRIV_KEY_CONST, END_PRIV_KEY_CONST, keyOut, len); +} + +bool prompt_user() +{ + printf("(y/n)\n"); + char ch = 0; + while(ch != 'y' && ch != 'n') { + if (read(0, (void*)&ch, 1)) { // 0 is stdin + //NOP + } + } + + return ch == 'y'; +} + +bool parse_asn1_signature(uint8_t *asn1Sig, size_t asn1SigLen, uint8_t signatureRintSintOUT[ATCA_SIG_SIZE]) +{ + if (asn1Sig == NULL || asn1SigLen < sizeof(signatureRintSintOUT)) return false; + + // Parse ASN.1. We expect the following output: + // 0x30 0xXX <--0x30 = sequence; 0xXX num bytes in sequence + // 0x02 0xYY <--0x02 = integer (R); 0xYY num bytes in R integer (2's complement) + // ... ... <-- YY bytes of R integer + // 0x02 0xZZ <--0x02 = integer (S); 0xZZ num bytes in S integer (2's complement) + // ... ... <-- ZZ bytes of S integer + const uint8_t halfSigSize = ATCA_SIG_SIZE / 2; + uint8_t *atca_sig_ptr = signatureRintSintOUT; + if (asn1Sig[0] != 0x30) + { // sequence + RETURN_BOOL(false, "Expected sequence code (0x30) at position 0"); + } + uint8_t seq_len = asn1Sig[1]; + if (seq_len < ATCA_SIG_SIZE) + { // sanity check + RETURN_BOOL(false, "Sequence does not seem to be long enough for a signature"); + } + if (asn1Sig[2] != 0x02) + { // integer + RETURN_BOOL(false, "Expected integer code (0x02) at position 2 (R integer)"); + } + // Get R integer + uint8_t r_len = asn1Sig[3]; + uint8_t *int_ptr = &asn1Sig[4]; + if (r_len < halfSigSize) + { + atca_sig_ptr += (halfSigSize - r_len); + } + else if (r_len > halfSigSize) + { + int_ptr += (r_len - halfSigSize); + r_len = halfSigSize; + } + memcpy(atca_sig_ptr, int_ptr, r_len); // <--insert R integer into output + atca_sig_ptr += r_len; + int_ptr += r_len; + // more checks + if (*int_ptr++ != 0x02) + { // integer + RETURN_BOOL(false, "Expected integer code (0x02) at next position (S integer)"); + } + // Get S integer + uint8_t s_len = *int_ptr++; + if (s_len < halfSigSize) + { + atca_sig_ptr += (halfSigSize - s_len); + } + else if (s_len > halfSigSize) + { + int_ptr += (s_len - halfSigSize); + s_len = halfSigSize; + } + memcpy(atca_sig_ptr, int_ptr, s_len); // <--insert S integer into output + return true; +} + +void print_b64_pubkey(uint8_t pubkey[ATCA_PUB_KEY_SIZE]) +{ + char b64pubkey[100] = {0}; + size_t b64len = sizeof(b64pubkey); + atcab_base64encode(pubkey, ATCA_PUB_KEY_SIZE, b64pubkey, &b64len); + printf(BEGIN_PUB_KEY_CONST"\r\n"); + printf(b64pubkey); + printf("\r\n"END_PUB_KEY_CONST"\r\n"); +} diff --git a/examples/atcatool/pubkeyprog.c b/examples/atcatool/pubkeyprog.c new file mode 100644 index 0000000..c1df2e1 --- /dev/null +++ b/examples/atcatool/pubkeyprog.c @@ -0,0 +1,245 @@ +/* pubkeyprog + * Program public keys in the ATCA over UART + * UART RX is interrupt driven + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +#include "i2c/i2c.h" +#include "cryptoauthlib_init.h" +#include + +#include "uart_cmds.h" + +static ATCA_STATUS cmd_cfg(uint32_t argc, char *argv[]) +{ + uint8_t configData[ATCA_ECC_CONFIG_SIZE]; + ATCA_STATUS status = atcab_read_config_zone(configData); + if(status != ATCA_SUCCESS) + { + RETURN(status, "Failed to retrieve config data"); + } + + printf("Got config data:\n"); + atcab_printbin(configData, sizeof(configData), true); + RETURN(status, "Done"); +} + +static ATCA_STATUS cmd_otp(uint32_t argc, char *argv[]) +{ + uint8_t otpData[ATCA_OTP_SIZE]; + ATCA_STATUS status = atcab_read_bytes_zone(ATCA_ZONE_OTP, 0, 0, otpData, sizeof(otpData)); + if(status != ATCA_SUCCESS) + { + RETURN(status, "Failed to retrieve OTP data"); + } + + printf("Got OTP data:\n"); + atcab_printbin(otpData, sizeof(otpData), true); + RETURN(status, "Done"); +} + +// Copied from unit tests +uint8_t test_ecc_configdata[ATCA_ECC_CONFIG_SIZE] = { + 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x04, 0x05, 0x06, 0x07, 0xEE, 0x00, 0x01, 0x00, + 0xC0, 0x00, 0x55, 0x00, 0x8F, 0x2F, 0xC4, 0x44, 0x87, 0x20, 0xC4, 0xF4, 0x8F, 0x0F, 0x8F, 0x8F, + 0x9F, 0x8F, 0x83, 0x64, 0xC4, 0x44, 0xC4, 0x64, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x1C, 0x00, 0x13, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x33, 0x00, + 0x1C, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x30, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x32, 0x00, 0x30, 0x00 +}; +uint8_t g_otp_data[ATCA_OTP_SIZE] = "This is test data that can be read back out of the chip!huzzah!"; + +static ATCA_STATUS cmd_cfgwrite(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status = atcab_write_config_zone(test_ecc_configdata); + if(status != ATCA_SUCCESS) + { + RETURN(status, "Failed to write ECC config data"); + } + + RETURN(status, "Done"); +} + +static ATCA_STATUS cmd_otpwrite(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status = atcab_write_bytes_zone(ATCA_ZONE_OTP, 0, 0, g_otp_data, ATCA_OTP_SIZE); + if(status != ATCA_SUCCESS) + { + RETURN(status, "Failed to write OTP data"); + } + + RETURN(status, "Done"); +} + +static ATCA_STATUS cmd_cfglock(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status = atcab_lock_config_zone(); + if (status != ATCA_SUCCESS) + { + printf("Failed!\n"); + } + + printf("Config Locked!\n"); + RETURN(status, "Done"); +} + +static ATCA_STATUS cmd_otplock(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status = atcab_lock_data_zone(); + if (status != ATCA_SUCCESS) + { + printf("Failed!\n"); + } + + printf("OTP Locked!\n"); + RETURN(status, "Done"); +} + +static ATCA_STATUS cmd_gen(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status; + if (argc >= 2) { + uint8_t slot_num = atoi(argv[1]); + if (slot_num > 0x7){ + LOG("Invalid slot number %d; must be between 0 and 7 for private keys", slot_num); + return ATCA_BAD_PARAM; + } + + uint8_t pubkey[ATCA_PUB_KEY_SIZE] = {0}; + if ((status = atcab_genkey(slot_num, pubkey)) != ATCA_SUCCESS){ + RETURN(status, "Could not generate private key"); + } + + atcab_printbin_label("publickey ", pubkey, sizeof(pubkey)); + print_b64_pubkey(pubkey); + RETURN(status, "Done"); + } else { + printf("Error: missing slot number.\n"); + return ATCA_BAD_PARAM; + } +} + +static ATCA_STATUS cmd_getpub(uint32_t argc, char *argv[]) +{ + ATCA_STATUS status; + if (argc >= 2) { + uint8_t slot_num = atoi(argv[1]); + if (slot_num > 0x7){ + LOG("Invalid slot number %d; must be between 0 and 7 for private keys", slot_num); + return ATCA_BAD_PARAM; + } + + uint8_t pubkey[ATCA_PUB_KEY_SIZE] = {0}; + if ((status = atcab_get_pubkey(slot_num, pubkey)) != ATCA_SUCCESS){ + RETURN(status, "Could not read/generate public key"); + } + + atcab_printbin_label("publickey ", pubkey, sizeof(pubkey)); + print_b64_pubkey(pubkey); + RETURN(status, "Done"); + } else { + printf("Error: missing slot number.\n"); + return ATCA_BAD_PARAM; + } +} + +static void cmd_help(uint32_t argc, char *argv[]) +{ + printf("pub Program public key to a slot\n"); + printf("priv [slot number] Program private key to a slot using optional write key\n"); + printf("cfg Dump config\n"); + printf("otp Dump OTP zone\n"); + printf("verify Verify signature with public key in slot\n"); + printf("cfgwrite Write test configuration\n"); + printf("otpwrite Write test OTP\n"); + printf("cfglock Lock config zone (not reversible!)\n"); + printf("otplock Lock OTP zone (not reversible!)\n"); + printf("gen Generate private key in a slot\n"); + printf("getpub Read/gen a public key from a private key slot\n"); + printf("ecdh Perform ECDH key exchange in slot\n"); + printf("\nExample:\n"); + printf(" pub 12 initiates public key programming in slot 12\n"); +} + +static void handle_command(char *cmd) +{ + char *argv[MAX_ARGC]; + int argc = 1; + char *temp, *rover; + memset((void*) argv, 0, sizeof(argv)); + argv[0] = cmd; + rover = cmd; + // Split string " ... " + // into argv, argc style + while(argc < MAX_ARGC && (temp = strstr(rover, " "))) { + rover = &(temp[1]); + argv[argc++] = rover; + *temp = 0; + } + + if (strlen(argv[0]) > 0) { + if (strcmp(argv[0], "help") == 0) cmd_help(argc, argv); + else if (strcmp(argv[0], "pub") == 0) cmd_pub(argc, argv); + else if (strcmp(argv[0], "priv") == 0) cmd_priv(argc, argv); + else if (strcmp(argv[0], "cfg") == 0) cmd_cfg(argc, argv); + else if (strcmp(argv[0], "otp") == 0) cmd_otp(argc, argv); + else if (strcmp(argv[0], "verify") == 0) cmd_verify(argc, argv); + else if (strcmp(argv[0], "cfgwrite") == 0) cmd_cfgwrite(argc, argv); + else if (strcmp(argv[0], "otpwrite") == 0) cmd_otpwrite(argc, argv); + else if (strcmp(argv[0], "cfglock") == 0) cmd_cfglock(argc, argv); + else if (strcmp(argv[0], "otplock") == 0) cmd_otplock(argc, argv); + else if (strcmp(argv[0], "gen") == 0) cmd_gen(argc, argv); + else if (strcmp(argv[0], "getpub") == 0) cmd_getpub(argc, argv); + else if (strcmp(argv[0], "ecdh") == 0) cmd_ecdh(argc, argv); + else printf("Unknown command %s, try 'help'\n", argv[0]); + } +} + +void pubkeyprog_task(void *pvParameters) +{ + char ch; + char cmd[81]; + int i = 0; + + init_cryptoauthlib(0x60); + + printf("\n\n\nWelcome to atcatool. Type 'help' for, well, help\n"); + printf("%% "); + while(1) { + if (read(0, (void*)&ch, 1)) { // 0 is stdin + printf("%c", ch); + if (ch == '\n' || ch == '\r') { + cmd[i] = 0; + i = 0; + printf("\n"); + handle_command((char*) cmd); + printf("%% "); + } else { + if (i < sizeof(cmd)) cmd[i++] = ch; + } + } + } +} + +#define I2C_BUS (0) +#define SDA_PIN (4) +#define SCL_PIN (5) + +void user_init(void) +{ + uart_set_baud(0, 115200); + i2c_init(I2C_BUS, SCL_PIN, SDA_PIN, I2C_FREQ_500K); + xTaskCreate(&pubkeyprog_task, "pubkeyprog_task", 2048, NULL, 2, NULL); +} diff --git a/examples/atcatool/uart_cmds.h b/examples/atcatool/uart_cmds.h new file mode 100644 index 0000000..0273b2b --- /dev/null +++ b/examples/atcatool/uart_cmds.h @@ -0,0 +1,24 @@ + +#include + +#define MAX_ARGC (10) +#define LOG(...) printf(__VA_ARGS__); printf("\r\n"); +#define LOG_SUCCESSFAIL(name, oper) if((oper) == ATCA_SUCCESS) { LOG("%s succeeded!\n", name); } else { LOG("%s failed!", name); } + +#define BEGIN_PUB_KEY_CONST "-----BEGIN PUBLIC KEY-----" +#define END_PUB_KEY_CONST "-----END PUBLIC KEY-----" + +#define BEGIN_PRIV_KEY_CONST "-----BEGIN EC PRIVATE KEY-----" +#define END_PRIV_KEY_CONST "-----END EC PRIVATE KEY-----" + +ATCA_STATUS cmd_pub(uint32_t argc, char *argv[]); +ATCA_STATUS cmd_priv(uint32_t argc, char *argv[]); +ATCA_STATUS cmd_verify(uint32_t argc, char *argv[]); +ATCA_STATUS cmd_ecdh(uint32_t argc, char *argv[]); + +// Utility +ATCA_STATUS read_pubkey_stdin(uint8_t *keyOut, int *len); +ATCA_STATUS read_privkey_stdin(uint8_t *keyOut, int *len); +bool prompt_user(); +bool parse_asn1_signature(uint8_t *asn1Sig, size_t asn1SigLen, uint8_t signatureRintSintOUT[ATCA_SIG_SIZE]); +void print_b64_pubkey(uint8_t pubkey[ATCA_PUB_KEY_SIZE]); diff --git a/extras/cryptoauthlib/README.md b/extras/cryptoauthlib/README.md new file mode 100644 index 0000000..9a81965 --- /dev/null +++ b/extras/cryptoauthlib/README.md @@ -0,0 +1,6 @@ +# cryptoauthlib + +Note: The file atca_iface.h is copied from cryptoauthlib/lib/atca_iface.h. The difference is, the anonymous struct names have been commented out, since they are not valid in c++ (only c11). Otherwise this file is exactly the same. Its presence here overrides the +one in cryptoauthlib. See . + +Also see for some tools which aid in generating config data for the ECC module. diff --git a/extras/cryptoauthlib/atca_iface.h b/extras/cryptoauthlib/atca_iface.h new file mode 100644 index 0000000..a9a427f --- /dev/null +++ b/extras/cryptoauthlib/atca_iface.h @@ -0,0 +1,173 @@ +/** + * \file + * + * \brief Microchip Crypto Auth hardware interface object + * + * \copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip software + * and any derivatives exclusively with Microchip products. It is your + * responsibility to comply with third party license terms applicable to your + * use of third party software (including open source software) that may + * accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, + * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE + * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF + * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE + * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL + * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED + * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR + * THIS SOFTWARE. + */ + +#ifndef ATCA_IFACE_H +#define ATCA_IFACE_H + +/** \defgroup interface ATCAIface (atca_) + * \brief Abstract interface to all CryptoAuth device types. This interface + * connects to the HAL implementation and abstracts the physical details of the + * device communication from all the upper layers of CryptoAuthLib + @{ */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "atca_command.h" + +typedef enum +{ + ATCA_I2C_IFACE, + ATCA_SWI_IFACE, + ATCA_UART_IFACE, + ATCA_SPI_IFACE, + ATCA_HID_IFACE, + ATCA_CUSTOM_IFACE, + // additional physical interface types here + ATCA_UNKNOWN_IFACE, +} ATCAIfaceType; + +/* ATCAIfaceCfg is a mediator object between a completely abstract notion of a + physical interface and an actual physical interface. + + The main purpose of it is to keep hardware specifics from bleeding into the + higher levels - hardware specifics could include things like framework + specific items (ASF SERCOM) vs a non-Microchip I2C library constant that + defines an I2C port. But I2C has roughly the same parameters regardless of + architecture and framework. + */ + +typedef struct +{ + + ATCAIfaceType iface_type; // active iface - how to interpret the union below + ATCADeviceType devtype; // explicit device type + + union // each instance of an iface cfg defines a single type of interface + { + struct //ATCAI2C + { + uint8_t slave_address; // 8-bit slave address + uint8_t bus; // logical i2c bus number, 0-based - HAL will map this to a pin pair for SDA SCL + uint32_t baud; // typically 400000 + } atcai2c; + + struct //ATCASWI + { + uint8_t bus; // logical SWI bus - HAL will map this to a pin or uart port + } atcaswi; + + struct //ATCAUART + { + int port; // logic port number + uint32_t baud; // typically 115200 + uint8_t wordsize; // usually 8 + uint8_t parity; // 0 == even, 1 == odd, 2 == none + uint8_t stopbits; // 0,1,2 + } atcauart; + + struct //ATCAHID + { + int idx; // HID enumeration index + uint32_t vid; // Vendor ID of kit (0x03EB for CK101) + uint32_t pid; // Product ID of kit (0x2312 for CK101) + uint32_t packetsize; // Size of the USB packet + uint8_t guid[16]; // The GUID for this HID device + } atcahid; + + struct //ATCACUSTOM + { + ATCA_STATUS (*halinit)(void *hal, void *cfg); + ATCA_STATUS (*halpostinit)(void *iface); + ATCA_STATUS (*halsend)(void *iface, uint8_t *txdata, int txlength); + ATCA_STATUS (*halreceive)(void *iface, uint8_t* rxdata, uint16_t* rxlength); + ATCA_STATUS (*halwake)(void *iface); + ATCA_STATUS (*halidle)(void *iface); + ATCA_STATUS (*halsleep)(void *iface); + ATCA_STATUS (*halrelease)(void* hal_data); + } atcacustom; + + }; + + uint16_t wake_delay; // microseconds of tWHI + tWLO which varies based on chip type + int rx_retries; // the number of retries to attempt for receiving bytes + void * cfg_data; // opaque data used by HAL in device discovery +} ATCAIfaceCfg; +typedef struct atca_iface * ATCAIface; + + +/** \brief atca_iface is the C object backing ATCAIface. See the atca_iface.h file for + * details on the ATCAIface methods + */ + +struct atca_iface +{ + ATCAIfaceType mType; + ATCAIfaceCfg *mIfaceCFG; // points to previous defined/given Cfg object, caller manages this + + ATCA_STATUS (*atinit)(void *hal, ATCAIfaceCfg *); + ATCA_STATUS (*atpostinit)(ATCAIface hal); + ATCA_STATUS (*atsend)(ATCAIface hal, uint8_t *txdata, int txlength); + ATCA_STATUS (*atreceive)(ATCAIface hal, uint8_t *rxdata, uint16_t *rxlength); + ATCA_STATUS (*atwake)(ATCAIface hal); + ATCA_STATUS (*atidle)(ATCAIface hal); + ATCA_STATUS (*atsleep)(ATCAIface hal); + + // treat as private + void *hal_data; // generic pointer used by HAL to point to architecture specific structure + // no ATCA object should touch this except HAL, HAL manages this pointer and memory it points to +}; + +ATCA_STATUS initATCAIface(ATCAIfaceCfg *cfg, ATCAIface ca_iface); +ATCAIface newATCAIface(ATCAIfaceCfg *cfg); +ATCA_STATUS releaseATCAIface(ATCAIface ca_iface); +void deleteATCAIface(ATCAIface *ca_iface); + +// IFace methods +ATCA_STATUS atinit(ATCAIface ca_iface); +ATCA_STATUS atpostinit(ATCAIface ca_iface); +ATCA_STATUS atsend(ATCAIface ca_iface, uint8_t *txdata, int txlength); +ATCA_STATUS atreceive(ATCAIface ca_iface, uint8_t *rxdata, uint16_t *rxlength); +ATCA_STATUS atwake(ATCAIface ca_iface); +ATCA_STATUS atidle(ATCAIface ca_iface); +ATCA_STATUS atsleep(ATCAIface ca_iface); + +// accessors +ATCAIfaceCfg * atgetifacecfg(ATCAIface ca_iface); +void* atgetifacehaldat(ATCAIface ca_iface); + + +#ifdef __cplusplus +} +#endif +/** @} */ +#endif + + + diff --git a/extras/cryptoauthlib/component.mk b/extras/cryptoauthlib/component.mk new file mode 100644 index 0000000..2aa6f31 --- /dev/null +++ b/extras/cryptoauthlib/component.mk @@ -0,0 +1,41 @@ +# +# esp_open_rtos build system component Makefile +# + +CRYPTOAUTHLIB_MODULE_DIR = $(cryptoauthlib_ROOT)cryptoauthlib/ +CRYPTOAUTHLIB_DIR = $(CRYPTOAUTHLIB_MODULE_DIR)lib/ +INC_DIRS += $(cryptoauthlib_ROOT) $(CRYPTOAUTHLIB_DIR) $(CRYPTOAUTHLIB_DIR)basic $(CRYPTOAUTHLIB_DIR)hal + +# args for passing into compile rule generation +cryptoauthlib_INC_DIR = $(CRYPTOAUTHLIB_DIR) $(CRYPTOAUTHLIB_DIR)basic +cryptoauthlib_SRC_DIR = \ + $(cryptoauthlib_ROOT) \ + $(CRYPTOAUTHLIB_DIR) \ + $(CRYPTOAUTHLIB_DIR)basic \ + $(CRYPTOAUTHLIB_DIR)crypto \ + $(CRYPTOAUTHLIB_DIR)crypto/hashes \ + $(CRYPTOAUTHLIB_DIR)host + +cryptoauthlib_EXTRA_SRC_FILES = $(CRYPTOAUTHLIB_DIR)hal/atca_hal.c $(cryptoauthlib_ROOT)/hal/atca_hal_espfreertos_i2c.c + +# default define variable values +CRYPTOAUTHLIB_ROOT_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(CRYPTOAUTHLIB_ROOT_DIR)defaults.mk + +ATEC_DEBUG_FLAGS = \ + -DATEC_HAL_DEBUG=$(ATEC_HAL_DEBUG) \ + -DATEC_HAL_VERBOSE_DEBUG=$(ATEC_HAL_VERBOSE_DEBUG) \ + -DATEC_I2C_HAL_DEBUG=$(ATEC_I2C_HAL_DEBUG) + +cryptoauthlib_CFLAGS += -DATCA_HAL_I2C -std=gnu11 $(ATEC_DEBUG_FLAGS) + +ifeq ($(ATEC_PRINTF_ENABLE), 1) +cryptoauthlib_CFLAGS += -DATCAPRINTF +cryptoauthlib_CXXFLAGS += -DATCAPRINTF +endif + +$(eval $(call component_compile_rules,cryptoauthlib)) + +# Helpful error if git submodule not initialised +$(CRYPTOAUTHLIB_MODULE_DIR): + $(error "cryptoauthlib git submodule not installed. Please run 'git submodule update --init'") diff --git a/extras/cryptoauthlib/cryptoauthlib b/extras/cryptoauthlib/cryptoauthlib new file mode 160000 index 0000000..f86b9db --- /dev/null +++ b/extras/cryptoauthlib/cryptoauthlib @@ -0,0 +1 @@ +Subproject commit f86b9dbb1e805d85a435456ca03450342dbdc43e diff --git a/extras/cryptoauthlib/cryptoauthlib_init.c b/extras/cryptoauthlib/cryptoauthlib_init.c new file mode 100644 index 0000000..0da98db --- /dev/null +++ b/extras/cryptoauthlib/cryptoauthlib_init.c @@ -0,0 +1,83 @@ +#include "cryptoauthlib.h" +#include "lwip/def.h" + +#include +#include + +#define ATEC_DEBUG +#ifdef ATEC_DEBUG + #define DBG(...) printf("%s:%d ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\r\n"); + #define DBGX(...) printf(__VA_ARGS__); +#else + #define DBGV(...) {}; + #define DBGVX(...) {}; +#endif + +void init_cryptoauthlib(uint8_t i2c_addr) +{ + uint32_t revision; + uint32_t serial[(ATCA_SERIAL_NUM_SIZE + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; + bool config_is_locked, data_is_locked; + ATCA_STATUS status; + const ATCAIfaceCfg *atca_cfg; + + /* + * Allow for addresses either in 7-bit format (0-7F) or in Atmel 8-bit shifted-by-one format. + * If user specifies address > 0x80, it must be already shifted since I2C bus addresses > 0x7f are invalid. + */ + if (i2c_addr < 0x7f) i2c_addr <<= 1; + atca_cfg = &cfg_ateccx08a_i2c_default; + if (atca_cfg->atcai2c.slave_address != i2c_addr) + { + ATCAIfaceCfg *cfg = (ATCAIfaceCfg *) calloc(1, sizeof(*cfg)); + memcpy(cfg, &cfg_ateccx08a_i2c_default, sizeof(*cfg)); + cfg->atcai2c.slave_address = i2c_addr; + atca_cfg = cfg; + } + + status = atcab_init(atca_cfg); + if (status != ATCA_SUCCESS) + { + DBG("ATCA: Library init failed"); + goto out; + } + + status = atcab_info((uint8_t *) &revision); + if (status != ATCA_SUCCESS) + { + DBG("ATCA: Failed to get chip info"); + goto out; + } + + status = atcab_read_serial_number((uint8_t *) serial); + if (status != ATCA_SUCCESS) + { + DBG("ATCA: Failed to get chip serial number"); + goto out; + } + + status = atcab_is_locked(LOCK_ZONE_CONFIG, &config_is_locked); + status = atcab_is_locked(LOCK_ZONE_DATA, &data_is_locked); + if (status != ATCA_SUCCESS) + { + DBG("ATCA: Failed to get chip zone lock status"); + goto out; + } + + DBG("ATECC508 @ 0x%02x: rev 0x%04x S/N 0x%04x%04x%02x, zone " + "lock status: %s, %s", + i2c_addr >> 1, htonl(revision), htonl(serial[0]), htonl(serial[1]), + *((uint8_t *) &serial[2]), (config_is_locked ? "yes" : "no"), + (data_is_locked ? "yes" : "no")); + +out: + /* + * We do not free atca_cfg in case of an error even if it was allocated + * because it is referenced by ATCA basic object. + */ + if (status != ATCA_SUCCESS) + { + DBG("ATCA: Chip is not available"); + /* In most cases the device can still work, so we continue anyway. */ + } +} diff --git a/extras/cryptoauthlib/cryptoauthlib_init.h b/extras/cryptoauthlib/cryptoauthlib_init.h new file mode 100644 index 0000000..fde91a2 --- /dev/null +++ b/extras/cryptoauthlib/cryptoauthlib_init.h @@ -0,0 +1,15 @@ + +#ifndef CRYPTOAUTHLIB_INIT_H +#define CRYPTOAUTHLIB_INIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +void init_cryptoauthlib(uint8_t i2c_addr); + +#ifdef __cplusplus +} //extern "C" { +#endif + +#endif //CRYPTOAUTHLIB_INIT_H diff --git a/extras/cryptoauthlib/defaults.mk b/extras/cryptoauthlib/defaults.mk new file mode 100644 index 0000000..2a70bd8 --- /dev/null +++ b/extras/cryptoauthlib/defaults.mk @@ -0,0 +1,15 @@ +######################################### +# Default define variables +######################################### + +# Enable/disable debug output from HAL +ATEC_HAL_DEBUG ?= 0 + +# Enable/disable verbose debug output from HAL +ATEC_HAL_VERBOSE_DEBUG ?= 0 + +# Enable/disable i2c debug output from HAL +ATEC_I2C_HAL_DEBUG ?= 0 + +# Enable/disable printf from library +ATEC_PRINTF_ENABLE ?= 0 diff --git a/extras/cryptoauthlib/hal/atca_hal_espfreertos_i2c.c b/extras/cryptoauthlib/hal/atca_hal_espfreertos_i2c.c new file mode 100644 index 0000000..48878a0 --- /dev/null +++ b/extras/cryptoauthlib/hal/atca_hal_espfreertos_i2c.c @@ -0,0 +1,372 @@ +#include +#include // sdk_os_delay_us + +#include "i2c/i2c.h" + +#include "cryptoauthlib.h" + +#include "FreeRTOS.h" +#include "task.h" + +// Move to config if ever necessary +#define I2C_BUS (0) + +#define vTaskDelayMs(ms) vTaskDelay((ms)/portTICK_PERIOD_MS) + +#if ATEC_HAL_DEBUG || ATEC_HAL_VERBOSE_DEBUG + #define DBG(...) printf("%s:%d ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\r\n"); + #define DBGX(...) printf(__VA_ARGS__); + #if ATEC_HAL_VERBOSE_DEBUG + #define DEBUG_HAL + #define DBGV(...) printf("%s:%d ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\r\n"); + #define DBGVX(...) printf(__VA_ARGS__); + #else + #define DBGV(...) {}; + #define DBGVX(...) {}; + #endif + + #ifdef DEBUG_HAL + static void print_array(uint8_t *data, uint32_t data_size) + { + uint32_t n; + + for (n = 0; n < data_size; n++) + { + printf("%.2x ", data[n]); + if (((n + 1) % 16) == 0) + { + printf("\r\n"); + if ((n + 1) != data_size) + printf(" "); + } + } + if (data_size % 16 != 0) + printf("\r\n"); + } + #endif +#else + #define DBG(...) {}; + #define DBGX(...) {}; + #define DBGV(...) {}; + #define DBGVX(...) {}; +#endif + +#if ATEC_I2C_HAL_DEBUG + #define I2C_DBG(...) printf(__VA_ARGS__); printf("\r\n"); +#else + #define I2C_DBG(...) {}; +#endif + +ATCA_STATUS hal_i2c_init(void *hal, ATCAIfaceCfg *cfg) +{ + (void)hal; + (void)cfg; + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_post_init(ATCAIface iface) +{ + (void)iface; + return ATCA_SUCCESS; +} + +static bool hal_internal_i2c_write(ATCAIface iface, uint8_t *txdata, int len) +{ + const ATCAIfaceCfg *cfg = atgetifacecfg(iface); + uint8_t slave_addr = (cfg->atcai2c.slave_address >> 1); + + int result = i2c_slave_write(I2C_BUS, slave_addr, NULL, txdata, len); + if (result != 0) + { + I2C_DBG("I2C write Error: %d len data: %d first byte: %x", result, len, len > 0 ? txdata[0] : 0); + return false; + } + + return true; +} + +static bool hal_internal_i2c_read(ATCAIface iface, uint8_t *rxdata, int len) +{ + const ATCAIfaceCfg *cfg = atgetifacecfg(iface); + uint8_t slave_addr = (cfg->atcai2c.slave_address >> 1); + + int result = i2c_slave_read(I2C_BUS, slave_addr, NULL, rxdata, len); + if (result != 0) + { + I2C_DBG("I2C read Error: %d len data: %d", result, len); + return false; + } + + return true; +} + +ATCA_STATUS hal_i2c_send(ATCAIface iface, uint8_t *txdata, int txlength) +{ + ATCA_STATUS status = ATCA_TX_TIMEOUT; +#ifdef DEBUG_HAL + // shamelessly taken from hal_sam4s_i2c_asf.c + printf("hal_i2c_send()\r\n"); + + printf("\r\nCommand Packet (size:0x%.8x)\r\n", (uint32_t)txlength); + printf("Count : %.2x\r\n", txdata[1]); + printf("Opcode : %.2x\r\n", txdata[2]); + printf("Param1 : %.2x\r\n", txdata[3]); + printf("Param2 : "); print_array(&txdata[4], 2); + if (txdata[1] > 7) { + printf("Data : "); print_array(&txdata[6], txdata[1] - 7); + } + printf("CRC : "); print_array(&txdata[txdata[1] - 1], 2); + printf("\r\n"); +#endif + + DBG("Send len %d, sending command", txlength); + txdata[0] = 0x03; /* Word Address Value = Command */ + txlength++; /* Include Word Address value in txlength */ + + if (hal_internal_i2c_write(iface, txdata, txlength)) + { + DBGV("ATCA_SUCCESS"); + status = ATCA_SUCCESS; + } + else + { + DBGV("ATCA_TX_FAIL"); + status = ATCA_TX_FAIL; + } + return status; +} + +ATCA_STATUS hal_i2c_receive(ATCAIface iface, uint8_t *rxdata, uint16_t *rxlength) +{ + DBGV("hal_i2c_receive()"); + const ATCAIfaceCfg *cfg = atgetifacecfg(iface); + + ATCA_STATUS status = ATCA_RX_TIMEOUT; + + int retries = cfg->rx_retries; + + while (retries-- > 0) + { + if (!hal_internal_i2c_read(iface, rxdata, *rxlength)) + { + DBGV("ATCA_RX_FAIL--retry %d", retries); + continue; + } + DBGV("ATCA_SUCCESS"); + status = ATCA_SUCCESS; + break; + } + +#ifdef DEBUG_HAL + printf("\r\nResponse Packet (size:0x%.4x)\r\n", rxlength); + printf("Count : %.2x\r\n", rxdata[0]); + if (rxdata[0] > 3) { + printf("Data : "); print_array(&rxdata[1], rxdata[0] - 3); + printf("CRC : "); print_array(&rxdata[rxdata[0] - 2], 2); + } + printf("\r\n"); +#endif + /* + * rxlength is a pointer, which suggests that the actual number of bytes + * received should be returned in the value pointed to, but none of the + * existing HAL implementations do it. + */ + return status; +} + +ATCA_STATUS hal_i2c_wake(ATCAIface iface) +{ + const ATCAIfaceCfg *cfg = atgetifacecfg(iface); + + ATCA_STATUS status = ATCA_WAKE_FAILED; + + uint8_t response[4] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t expected_response[4] = { 0x04, 0x11, 0x33, 0x43 }; + + /* + * ATCA devices define "wake up" token as START, 80 us of SDA low, STOP. + */ + DBGV("Sending wake"); + i2c_start(I2C_BUS); + atca_delay_us(80); + i2c_stop(I2C_BUS); + + /* After wake signal we need to wait some time for device to init. */ + atca_delay_us(cfg->wake_delay); + + /* Receive the wake response. */ + uint16_t len = sizeof(response); + status = hal_i2c_receive(iface, response, &len); + if (status == ATCA_SUCCESS) { + DBGV("Response %x %x %x %x", response[0], response[1], response[2], response[3]); + if (memcmp(response, expected_response, 4) != 0) { + DBGV("Wake failed"); + status = ATCA_WAKE_FAILED; + } + } + + return status; +} + +ATCA_STATUS hal_i2c_idle(ATCAIface iface) +{ + uint8_t idle_cmd = 0x02; + DBG("Sending idle"); + return hal_internal_i2c_write(iface, &idle_cmd, 1) ? ATCA_SUCCESS : ATCA_TX_FAIL; +} + +ATCA_STATUS hal_i2c_sleep(ATCAIface iface) +{ + uint8_t sleep_cmd = 0x01; + DBG("Sending sleep"); + return hal_internal_i2c_write(iface, &sleep_cmd, 1) ? ATCA_SUCCESS : ATCA_TX_FAIL; +} + +ATCA_STATUS hal_i2c_release(void *hal_data) +{ + (void)hal_data; + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_discover_buses(int i2c_buses[], int max_buses) +{ + i2c_buses[0] = 0; // There is just one bus on our esp8266 i2c implementation + return ATCA_SUCCESS; +} + +ATCA_STATUS hal_i2c_discover_devices(int busNum, ATCAIfaceCfg *cfg, int *found) +{ + ATCAIfaceCfg *head = cfg; + uint8_t slaveAddress = 0x01; + ATCADevice device; + ATCAIface discoverIface; + ATCACommand command; + ATCAPacket packet; + uint16_t rxsize; + uint32_t execution_or_wait_time; + ATCA_STATUS status; + uint8_t revs508[1][4] = { { 0x00, 0x00, 0x50, 0x00 } }; + uint8_t revs108[1][4] = { { 0x80, 0x00, 0x10, 0x01 } }; + uint8_t revs204[2][4] = { { 0x00, 0x02, 0x00, 0x08 }, + { 0x00, 0x04, 0x05, 0x00 } }; + int i; + + /** \brief default configuration, to be reused during discovery process */ + ATCAIfaceCfg discoverCfg; + discoverCfg.iface_type = ATCA_I2C_IFACE; + discoverCfg.devtype = ATECC508A; + discoverCfg.atcai2c.slave_address = 0x07; + discoverCfg.atcai2c.bus = busNum; + discoverCfg.atcai2c.baud = 400000; + //discoverCfg.atcai2c.baud = 100000; + discoverCfg.wake_delay = 800; + discoverCfg.rx_retries = 3; + + ATCAHAL_t hal; + + hal_i2c_init( &hal, &discoverCfg ); + device = newATCADevice( &discoverCfg ); + discoverIface = atGetIFace( device ); + command = atGetCommands( device ); + + // iterate through all addresses on given i2c bus + // all valid 7-bit addresses go from 0x07 to 0x78 + for ( slaveAddress = 0x07; slaveAddress <= 0x78; slaveAddress++ ) { + discoverCfg.atcai2c.slave_address = slaveAddress << 1; // turn it into an 8-bit address which is what the rest of the i2c HAL is expecting when a packet is sent + + // wake up device + // If it wakes, send it a dev rev command. Based on that response, determine the device type + // BTW - this will wake every cryptoauth device living on the same bus (ecc508a, sha204a) + + if ( hal_i2c_wake( discoverIface ) == ATCA_SUCCESS ) { + (*found)++; + memcpy( (uint8_t*)head, (uint8_t*)&discoverCfg, sizeof(ATCAIfaceCfg)); + + memset( packet.data, 0x00, sizeof(packet.data)); + + // get devrev info and set device type accordingly + atInfo( command, &packet ); +#ifdef ATCA_NO_POLL + if ((status = atGetExecTime(packet->opcode, device->mCommands)) != ATCA_SUCCESS) + { + return status; + } + execution_or_wait_time = device->mCommands->execution_time_msec; +#else + execution_or_wait_time = 1;//ATCA_POLLING_INIT_TIME_MSEC; +#endif + + // send the command + if ( (status = atsend( discoverIface, (uint8_t*)&packet, packet.txsize )) != ATCA_SUCCESS ) { + printf("packet send error\r\n"); + continue; + } + + // delay the appropriate amount of time for command to execute + atca_delay_ms(execution_or_wait_time); + + // receive the response + if ( (status = atreceive( discoverIface, &(packet.data[0]), &rxsize )) != ATCA_SUCCESS ) + continue; + + if ( (status = isATCAError(packet.data)) != ATCA_SUCCESS ) { + printf("command response error\r\n"); + continue; + } + + // determine device type from common info and dev rev response byte strings + for ( i = 0; i < (int)sizeof(revs508) / 4; i++ ) { + if ( memcmp( &packet.data[1], &revs508[i], 4) == 0 ) { + discoverCfg.devtype = ATECC508A; + break; + } + } + + for ( i = 0; i < (int)sizeof(revs204) / 4; i++ ) { + if ( memcmp( &packet.data[1], &revs204[i], 4) == 0 ) { + discoverCfg.devtype = ATSHA204A; + break; + } + } + + for ( i = 0; i < (int)sizeof(revs108) / 4; i++ ) { + if ( memcmp( &packet.data[1], &revs108[i], 4) == 0 ) { + discoverCfg.devtype = ATECC108A; + break; + } + } + + atca_delay_ms(15); + // now the device type is known, so update the caller's cfg array element with it + head->devtype = discoverCfg.devtype; + head++; + } + + hal_i2c_idle(discoverIface); + } + + hal_i2c_release(&hal); + + return ATCA_SUCCESS; +} + +void atca_delay_us(uint32_t us) +{ + DBG("atca_delay_us: %d", us); + + /* + * configTICK_RATE_HZ is 100, implying 10 ms ticks. + * But we run CPU at 160 and tick timer is not updated, hence / 2 below. + * https://github.com/espressif/ESP8266_RTOS_SDK/issues/90 + */ +#define USECS_PER_TICK (1000000 / configTICK_RATE_HZ / 2) + uint32_t ticks = us / USECS_PER_TICK; + us = us % USECS_PER_TICK; + if (ticks > 0) vTaskDelay(ticks); + sdk_os_delay_us(us); +} + +void atca_delay_ms(uint32_t ms) +{ + atca_delay_us(ms * 1000); +}