Dct_read_app_data works in v0.6.4 but not v0.6.3

I use code along the lines of this repo to read and upload Particle private keys when they get corrupted or erased on the device.

My code works great on v0.6.4 on the Electron. However it does not compile on v0.6.3 on the Photon, with error undefined reference to 'dct_read_app_data'.

Looking at the source code for those two versions, there is basically no difference that would impact the availability of that function. Why won’t this compile? @rickkas7 any thoughts? Seems like dct.h should be equally available in this fw version. I’d like to avoid manually calculating and accessing the raw flash address if possible.

The gist of my code is:

// .h
...
    #include "dct.h"
    // The size of the public and private keys depends on whether the device uses UDP (cellular devices, typically)
    // which use the ALT key slot, or TCP (Wi-Fi devices) which use the main key slot
    #if HAL_PLATFORM_CLOUD_UDP
    #define DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE     DCT_ALT_DEVICE_PRIVATE_KEY_SIZE
    #define DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE      DCT_ALT_DEVICE_PUBLIC_KEY_SIZE
    #define DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET   DCT_ALT_DEVICE_PRIVATE_KEY_OFFSET
    #define DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET    DCT_ALT_DEVICE_PUBLIC_KEY_OFFSET
    #define DEVICE_KEYS_HELPER_SERVER_KEY_OFFSET    DCT_ALT_SERVER_PUBLIC_KEY_OFFSET
    #define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE      DCT_ALT_SERVER_PUBLIC_KEY_SIZE + DCT_ALT_SERVER_ADDRESS_SIZE
    extern const uint8_t DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR[DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE];
    #else
    #define DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE     DCT_DEVICE_PRIVATE_KEY_SIZE
    #define DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE      DCT_DEVICE_PUBLIC_KEY_SIZE
    #define DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET   DCT_DEVICE_PRIVATE_KEY_OFFSET
    #define DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET    DCT_DEVICE_PUBLIC_KEY_OFFSET
    #define DEVICE_KEYS_HELPER_SERVER_KEY_OFFSET    DCT_SERVER_PUBLIC_KEY_OFFSET
    #define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE      DCT_SERVER_PUBLIC_KEY_SIZE + DCT_SERVER_ADDRESS_SIZE
    extern const uint8_t DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR[DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE];
    #endif
...
// .cpp
...
uint8_t* temp_key = new uint8_t[DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE];    // allocate on heap because too large for stack on Photon, delete below

// read key
#if SYSTEM_VERSION == 0x00070000
    SINGLE_THREADED_BLOCK() 
    {
        if ( dct_read_app_data_copy(DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET, temp_key, DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE) != 0 )
        {
            logging.Log(LOG_LVL_ERROR, LOG_GRP_SYSTEM, "Could not read particle key from flash");
            delete[] temp_key;
            return false;
        }
    }
#elif SYSTEM_VERSION == 0x00060400 || SYSTEM_VERSION == 0x00060300
    SINGLE_THREADED_BLOCK() 
    {
        const void* pkey_ptr = dct_read_app_data(DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET);
        if ( pkey_ptr == nullptr )
        {
            logging.Log(LOG_LVL_ERROR, LOG_GRP_SYSTEM, "Could not read particle key from flash");
            delete[] temp_key;
            return false;
        }
        memcpy(temp_key, pkey_ptr, DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE);
    }
#else
    delete[] temp_key;
    return false;
#endif

// then upload to my server to use cloud api to correct key...
...
1 Like

I just wrote my own library to access the DCD/DCT for firmware versions below v0.6.4. It should go without saying, but consider wrapping calls to these in a SINGLE_THREADED_BLOCK for safety.

No guarantees made on anything working properly, or for firmware v0.7.0 or higher (you should use the built in functions anyways then) so test in your use case.

dcd-reader.h

#ifndef DCD_READER_H
#define DCD_READER_H

#include "Particle.h"
#include "dct.h"

// Special Constants
#define DCT1_START 0x08004000
#define DCT2_START 0x08008000
#if HAL_PLATFORM_CLOUD_UDP
#define DCT_CONFIG_OFFSET 0x08
#define DCT_ACTIVE_OFFSET 0x04
#else
#define DCT_CONFIG_OFFSET 0x1d7c
#define DCT_ACTIVE_OFFSET 0x09
#endif
#define DCT_CONFIG_AREA_LENGTH 4258


// The size of the public and private keys depends on whether the device uses UDP (cellular devices, typically)
// which use the ALT key slot, or TCP (Wi-Fi devices) which use the main key slot
#if HAL_PLATFORM_CLOUD_UDP
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE     DCT_ALT_DEVICE_PRIVATE_KEY_SIZE
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE      DCT_ALT_DEVICE_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET   DCT_ALT_DEVICE_PRIVATE_KEY_OFFSET
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET    DCT_ALT_DEVICE_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_SIZE    DCT_ALT_SERVER_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_SERVER_KEY_OFFSET    DCT_ALT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_ADDR_SIZE    DCT_ALT_SERVER_ADDRESS_SIZE
#define DEVICE_KEYS_HELPER_SERVER_ADDR_OFFSET    DCT_ALT_SERVER_ADDRESS_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_OFFSET      DCT_ALT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE      DCT_ALT_SERVER_PUBLIC_KEY_SIZE + DCT_ALT_SERVER_ADDRESS_SIZE
#else
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE     DCT_DEVICE_PRIVATE_KEY_SIZE
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE      DCT_DEVICE_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET   DCT_DEVICE_PRIVATE_KEY_OFFSET
#define DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET    DCT_DEVICE_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_SIZE    DCT_SERVER_PUBLIC_KEY_SIZE
#define DEVICE_KEYS_HELPER_SERVER_KEY_OFFSET    DCT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_ADDR_SIZE    DCT_SERVER_ADDRESS_SIZE
#define DEVICE_KEYS_HELPER_SERVER_ADDR_OFFSET    DCT_SERVER_ADDRESS_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_OFFSET      DCT_SERVER_PUBLIC_KEY_OFFSET
#define DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE      DCT_SERVER_PUBLIC_KEY_SIZE + DCT_SERVER_ADDRESS_SIZE
#endif

// errors
#define DCD_ERROR_CONFIG_NOT_FOUND 0x01
#define DCD_ERROR_SIZE_IS_ZERO 0x02
#define DCD_ERROR_ITEM_EXCEEDS_MEMORY 0x03
#define DCD_ERROR_BUFFER_TOO_SMALL 0x04
#define DCD_ERROR_BUFFER_TOO_LARGE 0x05
#define DCD_ERROR_CANT_ID_ACTIVE_DCT 0x05

enum DcdConfigType {
  VERSION,
  DEVICE_PRIVATE_KEY,
  DEVICE_PUBLIC_KEY,
  IP_CONFIG,
  CLAIM_CODE,
  CLAIMED,
  SSID_PREFIX,
  DEVICE_CODE,
  VERSION_STRING,
  SERVER_PUBLIC_KEY,
  SERVER_ADDRESS,
  SERVER_KEY_AND_ADDR,
  DEVICE_ID,
};

class DcdReader {
  public:
    DcdReader() {};
    ~DcdReader() {};

    int get_config(DcdConfigType config, void* read_ptr);
    int copy_config(DcdConfigType config, void* buf, size_t buf_len);

    int get_offset(uint32_t offset, void* read_ptr);
    int copy_offset(uint32_t offset, size_t size, void* buf, size_t buf_len);

  private:
    enum DctAddr {
        DCT_ERROR = 0,
        DCT1 = DCT1_START,
        DCT2 = DCT2_START
    };
    struct DcdConfigLocation {
        uint32_t offset;
        size_t size;
    };

    int check_offset(uint32_t offset, size_t size);
    DctAddr get_active_dct();
    DcdConfigLocation get_config_location(DcdConfigType config);
};

extern DcdReader Dcd;

#endif

dcd-reader.cpp

#include "dcd-reader.h"

int DcdReader::get_config(DcdConfigType config, void* read_ptr) {
    DcdConfigLocation config_location = get_config_location(config);
    if (config_location.size <= 0) return DCD_ERROR_CONFIG_NOT_FOUND;
    return get_offset(config_location.offset, read_ptr);
}

int DcdReader::copy_config(DcdConfigType config, void* buf, size_t buf_len) {
    DcdConfigLocation config_location = get_config_location(config);
    if (config_location.size > 0) {
        return copy_offset(config_location.offset, config_location.size, buf, buf_len);
    }
    return DCD_ERROR_CONFIG_NOT_FOUND;
}

int DcdReader::get_offset(uint32_t offset, void* read_ptr) {
    int err = check_offset(offset, 1); // use 1 as minimum valid case
    if (!err) {
        read_ptr = nullptr;
        return err;   
    }
    DctAddr active_dct = get_active_dct();
    if (active_dct == DCT_ERROR) {
        read_ptr = nullptr;
        return DCD_ERROR_CANT_ID_ACTIVE_DCT;
    }
    read_ptr = (void*)(active_dct + DCT_CONFIG_OFFSET + offset);
    return 0;
}

int DcdReader::copy_offset(uint32_t offset, size_t size, void* buf, size_t buf_len) {
    int err = check_offset(offset, size);
    if (!err) return err;
    if (buf_len < size) {
        return DCD_ERROR_BUFFER_TOO_SMALL;
    }
    DctAddr active_dct = get_active_dct();
    if (active_dct == DCT_ERROR) return DCD_ERROR_CANT_ID_ACTIVE_DCT;
    memcpy(buf, (void*)(active_dct + DCT_CONFIG_OFFSET + offset), size);
    return 0;
}

int DcdReader::check_offset(uint32_t offset, size_t size) {
    if (size <= 0) {
        return DCD_ERROR_SIZE_IS_ZERO;
    }
    if ((offset+size) > DCT_CONFIG_AREA_LENGTH) {
        return DCD_ERROR_ITEM_EXCEEDS_MEMORY;
    }
    return 0;
}

DcdReader::DcdConfigLocation DcdReader::get_config_location(DcdConfigType config) {
    DcdConfigLocation config_location;
    switch(config) {
        case VERSION:
            config_location.offset = 32;
            config_location.size = 2;
            break;
        case DEVICE_PRIVATE_KEY:
            config_location.offset = DEVICE_KEYS_HELPER_PRIVATE_KEY_OFFSET;
            config_location.size = DEVICE_KEYS_HELPER_PRIVATE_KEY_SIZE;
            break;
        case DEVICE_PUBLIC_KEY:
            config_location.offset = DEVICE_KEYS_HELPER_PUBLIC_KEY_OFFSET;
            config_location.size = DEVICE_KEYS_HELPER_PUBLIC_KEY_SIZE;
            break;
        case IP_CONFIG:
            config_location.offset = DCT_IP_CONFIG_OFFSET;
            config_location.size = DCT_IP_CONFIG_SIZE;
            break;
        case CLAIM_CODE:// claim code. no terminating null.
            config_location.offset = DCT_CLAIM_CODE_OFFSET;
            config_location.size = DCT_CLAIM_CODE_SIZE;
            break;
        case CLAIMED:   // 0,0xFF, not claimed. 1 claimed.
            config_location.offset = 1825;
            config_location.size = 1;
            break;
        case SSID_PREFIX:// SSID prefix (25 chars max). First byte is length.
            config_location.offset = DCT_SSID_PREFIX_OFFSET;
            config_location.size = DCT_SSID_PREFIX_SIZE;
            break;
        case DEVICE_CODE:// 6 suffix characters (not null terminated))
            config_location.offset = DCT_DEVICE_CODE_OFFSET;
            config_location.size = DCT_DEVICE_CODE_SIZE;
            break;
        case VERSION_STRING:
            config_location.offset = 1858;
            config_location.size = 32;
            break;
        case SERVER_PUBLIC_KEY:
            config_location.offset = DCT_SERVER_PUBLIC_KEY_OFFSET;
            config_location.size = DCT_SERVER_PUBLIC_KEY_SIZE;
            break;
        case SERVER_ADDRESS:
            config_location.offset = DEVICE_KEYS_HELPER_SERVER_ADDR_OFFSET;
            config_location.size = DEVICE_KEYS_HELPER_SERVER_ADDR_SIZE;
            break;
        case SERVER_KEY_AND_ADDR:
            config_location.offset = DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_OFFSET;
            config_location.size = DEVICE_KEYS_HELPER_SERVER_KEY_AND_ADDR_SIZE;
            break;
        case DEVICE_ID:
            config_location.offset = DCT_DEVICE_ID_OFFSET;
            config_location.size = DCT_DEVICE_ID_SIZE;
            break;
        default:
            config_location.offset = 0;
            config_location.size = 0;
    }
    return config_location;
}

DcdReader::DctAddr DcdReader::get_active_dct() {
    #if HAL_PLATFORM_CLOUD_UDP  
    const uint32_t dct1_byte = *(uint32_t*)(DCT1_START+DCT_ACTIVE_OFFSET);
    const uint32_t dct2_byte = *(uint32_t*)(DCT2_START+DCT_ACTIVE_OFFSET);
    if (dct1_byte > 0) return DCT1;
    else if (dct2_byte > 0) return DCT2;
    #else
    const uint8_t dct1_byte = *(uint8_t*)(DCT1_START+DCT_ACTIVE_OFFSET);
    const uint8_t dct2_byte = *(uint8_t*)(DCT2_START+DCT_ACTIVE_OFFSET);
    if (dct1_byte == 1) return DCT1;
    else if (dct2_byte == 1) return DCT2;
    #endif
    return DCT_ERROR;
}

DcdReader Dcd;