espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.73k stars 7.3k forks source link

EspTouch with AES encryption (IDFGH-732) #3172

Closed swanav closed 5 years ago

swanav commented 5 years ago

The ESPTouch library for Android and iOS includes a class called EspAes for AES encryption while using EspTouch. But there are no references to the same AES functionality in the ESP-IDF api reference or any of the examples. How can I implement the AES functionality in my firmware?

TimXia commented 5 years ago

@projectgus Please help.

swanav commented 5 years ago

Anything?

igrr commented 5 years ago

Hi @swanav, ESP-IDF includes mbedtls library. Please refer to mbedtls API reference for AES functions: https://tls.mbed.org/api/aes_8h.html

negativekelvin commented 5 years ago

What he wants to know is how to enable encryption in smartconfig on the esp32 side

projectgus commented 5 years ago

Hi @swanav ,

If you use the AES functions in the Esp-Touch library to encrypt the SSID & Password before sending then it will use a relatively simple block cipher to obfuscate the values: AES ECB mode with PKCS7 padding.

When you get the ciphertext values for SSID & Password back from the SmartConfig library on the ESP32 side, you can get back the plaintext using some code like this:

#include <string.h>
#include "esp_log.h"
#include "mbedtls/cipher.h"

const char *TAG = "esptouch decrypt";

static int decrypt_parameter(const unsigned char *ciphertext, const unsigned char *key, size_t len, unsigned char *plaintext)
{
    int r;
    size_t olen;
    mbedtls_cipher_context_t ctx;
    const mbedtls_cipher_info_t *info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB);

    if (len % 16 != 0) {
        ESP_LOGE(TAG, "len must be a multiple of block size");
        return -1;
    }

    mbedtls_cipher_init(&ctx);

    r = mbedtls_cipher_setup(&ctx, info);
    if (r != 0) {
        ESP_LOGE(TAG, "mbedtls_cipher_setup failed -%x", -r);
        goto done;
    }

    r = mbedtls_cipher_setkey(&ctx, key, 128, MBEDTLS_DECRYPT);
    if (r != 0) {
        ESP_LOGE(TAG, "mbedtls_cipher_setkey failed -%x", -r);
        goto done;
    }

    r = mbedtls_cipher_reset(&ctx);
    if (r != 0) {
        ESP_LOGE(TAG, "mbedtls_cipher_reset failed -%x", -r);
        goto done;
    }

    olen = 0;
    for (int i = 0; i < len; i += 16) {
        size_t olen_block = 16;
        r = mbedtls_cipher_update(&ctx, ciphertext + i, 16, plaintext + i, &olen_block);
        if (r != 0) {
            ESP_LOGE(TAG, "mbedtls_cipher_update failed -%x", -r);
            goto done;
        }
        olen += olen_block;
    }

    //ESP_LOG_BUFFER_HEX(TAG, plaintext, olen);

    // Check and remove PKCS7 padding
    uint8_t pad_len = plaintext[olen-1];
    uint8_t dlen = olen - pad_len;
    if (pad_len >= olen || dlen >= olen) {
        ESP_LOGE(TAG, "Bad PKCS7 padding byte: %d", pad_len);
        r = MBEDTLS_ERR_CIPHER_INVALID_PADDING;
        goto done;
    }
    for (int i = dlen; i < olen; i++) {
        if (plaintext[i] != pad_len) {
            ESP_LOGE(TAG, "Bad PKCS7 padding byte at offset %d", i);
            r = MBEDTLS_ERR_CIPHER_INVALID_PADDING;
            goto done;
        }
        plaintext[i] = 0;
    }

    ESP_LOGI(TAG, "decrypt successful");
    r = dlen;

 done:
    mbedtls_cipher_free(&ctx);
    return r;
}

void app_main()
{
    const unsigned char key[16] = "secret key!!"; // will 0-pad to 16 bytes
    unsigned char ciphertext[32] = {
        0x1a, 0x8a, 0x97, 0x89, 0xb7, 0x1e, 0xa4, 0x36, 0x08, 0x53, 0x25, 0x21, 0x5d, 0x53, 0xe5, 0x0d, 0x77, 0x67, 0x8b, 0xa1, 0xde, 0x56, 0x38, 0xdb, 0x20, 0x0e, 0x52, 0xf4, 0x60, 0xe8, 0xfa, 0xd2, };
    unsigned char plaintext[32] = { 0 }; // note: must be same size as ciphertext buffer

    int r = decrypt_parameter(ciphertext, key, sizeof(ciphertext), plaintext);
    ESP_LOGI(TAG, "decrypt result %d", r);
    ESP_LOGI(TAG, "plaintext as a string: %s\n", plaintext);
    printf("Done");
}

(Note: The above de-padding code leaks some timing information which could be used to find length of SSID/password in bytes. If you need that level of security then you may want to consider using a different cipher combination in app & firmware as well.)

projectgus commented 5 years ago

(I'm going to close this, but please comment and will reopen if the code above isn't working.)

negativekelvin commented 5 years ago

@projectgus Ok but the fact that you can just pass the aes key on the client side means it should be just as easy on the device side

https://github.com/EspressifApp/EsptouchForAndroid/blob/c9394fec60892bbbdc30aa940bf5e722bde87884/esptouch/src/main/java/com/espressif/iot/esptouch/EsptouchTask.java#L34-L43

Ok I see it is private and there is this note:

Hide EsptouchTask's aes constructor, device doesn't support currently

Time to fix?

projectgus commented 5 years ago

@negativekelvin For now, using AES in EspTouch will require some hacking of the library. We are discussing about adding some more complete encryption support, but possibly this will use a different cipher scheme.

hansmbakker commented 5 years ago

There is a research paper concluding that the use of Esptouch creates a security risk since it does not use encryption by default: https://loccs.sjtu.edu.cn/~romangol/publications/wisec18.pdf

It would be good if Espressif would implement fixes here.

GregDavid123 commented 3 years ago

Hi @projectgus,

thank you for the great solution. I am using Platformio and i will like to use the AES key in Esptouch V2 with Esp32. Has this solution already been updated in the present Wifi.h and SmartConfig libary? Is there an example or a guideline of how I can implement it?

That will be great thanks and Have a nice day :)

offgrid88 commented 1 year ago

I know this is a bit old but, I felt it is important to reply. The AES encryption is available through ESPTOUCH_V2

typedef struct {
    bool enable_log;                /**< Enable smartconfig logs. */
    bool esp_touch_v2_enable_crypt; /**< Enable ESPTouch v2 crypt. */
    char *esp_touch_v2_key;         /**< ESPTouch v2 crypt key, len should be 16. */
} smartconfig_start_config_t;

#define SMARTCONFIG_START_CONFIG_DEFAULT() { \
    .enable_log = false, \
    .esp_touch_v2_enable_crypt = false,\
    .esp_touch_v2_key = NULL \
};