本文介绍Link SDK v3.x 集成ID²的操作步骤和示例代码的运行过程。

Link SDK v3.x 集成ID²原理如下图所示。SSL_Wrapper

操作步骤

  1. 基于Link SDK代码抽取机制,完成SDK功能代码的抽取和HAL接口适配。
  2. 移植安全连接协议iTLS和ID²至目标平台。其中,OSA和HAL需要厂商根据接口进行适配;其他模块,由厂商提供编译工具,阿里云IoT进行适配。移植全流程如下图。ID²
  3. 适配Link SDK的HAL_Crypt_xxx。适配过程中可调用ID²中的Crypto模块,示例代码如下:
      #include "infra_compat.h"
      #include "ali_crypto.h"
    
      #define AES_BLOCK_SIZE 16
    
      #define KEY_LEN 16 // aes 128 cbc
      #define p_aes128_t p_HAL_Aes128_t
      #define PLATFORM_AES_ENCRYPTION HAL_AES_ENCRYPTION
      #define PLATFORM_AES_DECRYPTION HAL_AES_DECRYPTION
    
     void *HAL_Malloc(uint32_t size);
     void HAL_Free(void *ptr);
    
     p_HAL_Aes128_t HAL_Aes128_Init(
                 const uint8_t *key,
                 const uint8_t *iv,
                 AES_DIR_t dir)
     {
         ali_crypto_result result;
         void *            aes_ctx;
         size_t            aes_ctx_size, alloc_siz;
         uint8_t *         p;
         bool              is_en = true; // encrypto by default
    
         if (dir == PLATFORM_AES_DECRYPTION) {
             is_en = false;
         }
    
         result = ali_aes_get_ctx_size(AES_CBC, &aes_ctx_size);
         if (result != ALI_CRYPTO_SUCCESS) {
             HAL_Printf("get ctx size fail(%08x)", result);
             return NULL;
         }
    
         alloc_siz = aes_ctx_size + KEY_LEN * 2 + sizeof(bool);
         aes_ctx  = HAL_Malloc(alloc_siz);
         if (aes_ctx == NULL) {
             HAL_Printf("kmalloc(%d) fail", (int)aes_ctx_size);
             return NULL;
         }
         memset(aes_ctx, 0, alloc_siz);
    
         p = (uint8_t *)aes_ctx + aes_ctx_size;
         memcpy(p, key, KEY_LEN);
         p += KEY_LEN;
         memcpy(p, iv, KEY_LEN);
         p += KEY_LEN;
         *((bool *)p) = is_en;
    
         return aes_ctx;
     }
    
     int HAL_Aes128_Destroy(p_HAL_Aes128_t aes)
     {
         if (aes) {
             HAL_Free(aes);
         }
         return 0;
     }
    
     static int platform_aes128_encrypt_decrypt(p_HAL_Aes128_t aes_ctx,
                                                const void *src, size_t siz,
                                                void *dst, aes_type_t t)
     {
         ali_crypto_result result;
         size_t            dlen, in_len = siz, ctx_siz;
         uint8_t *         p, *key, *iv;
         bool              is_en;
         if (aes_ctx == NULL) {
             HAL_Printf("platform_aes128_encrypt_decrypt aes_ctx is NULL");
             return -1;
         }
         result = ali_aes_get_ctx_size(AES_CBC, &ctx_siz);
         if (result != ALI_CRYPTO_SUCCESS) {
             HAL_Printf("get ctx size fail(%08x)", result);
             return 0;
         }
    
         p   = (uint8_t *)aes_ctx + ctx_siz;
         key = p;
         p += KEY_LEN;
         iv = p;
         p += KEY_LEN;
         is_en = *((uint8_t *)p);
    
         in_len <<= t == AES_CBC ? 4 : 0;
         dlen = in_len;
    
         result = ali_aes_init(t, is_en, key, NULL, KEY_LEN, iv, aes_ctx);
         if (result != ALI_CRYPTO_SUCCESS) {
             HAL_Printf("ali_aes_init fail(%08x)", result);
             return 0;
         }
    
         result = ali_aes_finish(src, in_len, dst, &dlen, SYM_NOPAD, aes_ctx);
         if (result != ALI_CRYPTO_SUCCESS) {
             HAL_Printf("aes_finish fail(%08x)", result);
             return -1;
        }
        return 0;
    }
    
    int HAL_Aes128_Cbc_Encrypt(
                p_HAL_Aes128_t aes,
                const void *src,
                size_t blockNum,
                void *dst)
    {
        return platform_aes128_encrypt_decrypt(aes, src, blockNum, dst, AES_CBC);
    }
    
    int HAL_Aes128_Cbc_Decrypt(
                p_HAL_Aes128_t aes,
                const void *src,
                size_t blockNum,
                void *dst)
    {
        return platform_aes128_encrypt_decrypt(aes, src, blockNum, dst, AES_CBC);
    }
    
    int HAL_Aes128_Cfb_Encrypt(
                p_HAL_Aes128_t aes,
                const void *src,
                size_t length,
                void *dst)
    {
        return platform_aes128_encrypt_decrypt(aes, src, length, dst, AES_CFB128);
    }
    
    int HAL_Aes128_Cfb_Decrypt(
                _IN_ p_HAL_Aes128_t aes,
                _IN_ const void *src,
                _IN_ size_t length,
                _OU_ void *dst)
    {
        return platform_aes128_encrypt_decrypt(aes, src, length, dst, AES_CFB128);
    }
            
  4. 适配Link SDK的SSL HAL。
    1. 添加HAL_TLS_itls.c和HAL_DTLS_itls.c至eng/wrappers/tls目录。
    2. 删除eng/wrappers/tls目录下的HAL_TLS_mbedtls.c和HAL_DTLS_mbedtls.c文件。
  5. 集成Link SDK ID²库。
    1. 添加头文件和静态库。

      添加ID²相关头文件到eng/wrappers/include目录。

      添加ID²相关静态库release/lib目录。

    2. 修改编译脚本Makefile。
      链接ID²相关的静态库。
      CFLAGS  += -DSUPPORT_ITLS
      LDFLAGS += -L./release/lib -litls -lid2 -lkm -licrypt -lls_hal -lls_osa
      移除默认mbedtls模块的链接。
      WRAPPER_IMPL_C := $(shell find $(SRCDIR) -name "*.c" -path "*wrappers*" -not -path "*mbedtls*")

运行示例代码

  1. wrappers/os/xxx/HAL_OS_xxx.c中设置参数值,如下所示。
    char _product_key[IOTX_PRODUCT_KEY_LEN + 1]       = "a1MZxOd****";
    char _product_secret[IOTX_PRODUCT_SECRET_LEN + 1] = "h4I4dneEFp7E****";
    char _device_name[IOTX_DEVICE_NAME_LEN + 1]       = "test_01";
    char _device_secret[IOTX_DEVICE_SECRET_LEN + 1]   = "t9G******";       
    说明 product_key和product_secret在产品创建(选择开通ID²服务)时生成;device_name设备编号,使用ID²服务时,由厂商自行确定,需保证产品维度内的唯一性;device_secret设备密钥,使用ID²服务时,可设置为任意合法的字符串。
  2. 在样例程序中,配置ID²服务。

    以examples/mqtt_example.c为例。

         /**
          *
          *  MQTT connect hostname string
          *
          *  MQTT server's hostname can be customized here
          *
          *  default value is ${productKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com
          */
         /* mqtt_params.host = "something.iot-as-mqtt.cn-shanghai.aliyuncs.com"; */
    
        char host[64] = {0};
    
        HAL_Snprintf(host, 64, "%s.itls.cn-shanghai.aliyuncs.com", DEMO_PRODUCT_KEY);
        mqtt_params.host = host;
        mqtt_params.port = 1883;
        mqtt_params.customize_info = "authtype=id2";       
  3. 在SDK根目录,执行make命令,完成样例程序的编译。
    make clean
    make        
  4. 在已烧录ID²的设备上运行样例程序mqtt example。确认运行日志如下。
     [inf] Connecting to /a1WO4Z9****.itls.cn-shanghai.aliyuncs.com/1883...
     [inf]  ok
     [inf]   . Setting up the SSL/TLS structure...
     [inf]  ok
     <LS_LOG> ID2 Client Version: 0x00020000
     <LS_LOG> ID2 Client Build Time: Sep 26 2019 15:29:43
     <LS_LOG> -------------------------------------------
     <LS_LOG> CONFIG_ID2_DEBUG is not defined!
     <LS_LOG> CONFIG_ID2_OTP is defined!
    <LS_LOG> CONFIG_ID2_KEY_TYPE: ID2_KEY_TYPE_AES
    <LS_LOG> -------------------------------------------
    [inf] Performing the SSL/TLS handshake...
    <LS_LOG> id2_client_get_id 655: ID2: 00FFFFFFFFFFFFF20A1C****
    <LS_LOG> mbedtls_parse_auth_code_ext 407:   . Verify iTLS Server authCode OK!
    [inf]  ok
    ....
    < {
    <     "message": "hello!"
    < }
    
    example_message_arrive|031 :: Message Arrived:
    example_message_arrive|032 :: Topic  : /a1WO4Z9****/example1/user/get
    example_message_arrive|033 :: Payload: {"message":"hello!"}     

ID²相关调试

  • iTLS建连失败时,首先通过查看消息警告(alert message)进行问题排查。
     [inf] Connecting to /a1WO4Z9****.itls.cn-shanghai.aliyuncs.com/1883...
     [inf]  ok
     [inf]   . Setting up the SSL/TLS structure...
     [inf]  ok
     <LS_LOG> ID2 Client Version: 0x00020000
     <LS_LOG> ID2 Client Build Time: Sep 26 2019 15:29:43
     <LS_LOG> -------------------------------------------
     <LS_LOG> CONFIG_ID2_DEBUG is not defined!
     <LS_LOG> CONFIG_ID2_OTP is defined!
    <LS_LOG> CONFIG_ID2_KEY_TYPE: ID2_KEY_TYPE_AES
    <LS_LOG> -------------------------------------------
    [inf] Performing the SSL/TLS handshake...
    <LS_LOG> id2_client_get_id 655: ID2: 00FFFFFFFFFFFFA6F67A****
    <LS_LOG> mbedtls_ssl_handle_message_type 4194: got an alert message, type: [2:162]
    [err] failed  ! mbedtls_ssl_handshake returned -0x7780
            
    上述样例日志中,2表示FATAL类型的警告,162表示消息警告类型。iTLS常见的错误警告类型如下表:
    错误码 错误信息
    0 Peer close notify
    10 Unexpected message
    20 Bad record mac
    50 Decode error
    51 Decryption error
    110 Unsupported extension
    160 ID2 generic error
    161 ID2 no quota
    162 ID2 is not exist
    163 ID2 authcode is invalid
    164 ID2 has not been activated
    165 The timestamp used in authcode is expired
    166 ID2 challenge is invalid
    167 Not support this operation
    168 ID2 has been suspended
    169 ID2 has been discarded
    170 Permission denied, id2 has been blinded to other product key
    171 Product key is invalid
    172 Product key is not exist
    173 ID2 server is busy
    174 The device fingerprint is invalid
    175 The device fingerprint is duplicated
    176 ID2 server random is invalid
    177 Hash type used in authcode generated is invalid
    178 ID2 key type is invalid
  • 若通过查看消息警告不能确定错误原因,可使用iTLS和ID²调试版本进行调试。
    1. 使用iTLS和ID²调试库替换SDK中的正式库。
    2. 在HAL_xx_itls.c中配置调试级别宏(DEBUG_LEVEL)。
    /*< set debug log level, 0 close*/
    #define DEBUG_LEVEL     0        

    上述样例中0表示No debug,日志关闭,只有极少错误信息输出。

    DEBUG_LEVEL参数说明如下:

    参数值 描述
    0 No debug,日志关闭,只有极少错误信息输出。
    1 Error,错误日志。
    2 State change,错误日志和状态日志。
    3 Informational,错误日志、状态日志和调试日志。
    4 Verbose,输出所有日志。