/* * Tencent is pleased to support the open source community by making IoT Hub available. * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://opensource.org/licenses/MIT * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language governing permissions and * limitations under the License. * */ #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include "lite-utils.h" #include "qcloud_iot_ca.h" #include "qcloud_iot_common.h" #include "qcloud_iot_device.h" #include "qcloud_iot_export.h" #include "qcloud_iot_import.h" #include "utils_aes.h" #include "utils_base64.h" #include "utils_hmac.h" #include "utils_httpc.h" #include "ql_fs.h" #define REG_URL_MAX_LEN (128) #define DYN_REG_SIGN_LEN (64) #define DYN_BUFF_DATA_MORE (10) #define BASE64_ENCODE_OUT_LEN(x) (((x + 3) * 4) / 3) #define DYN_REG_RES_HTTP_TIMEOUT_MS (2000) #ifdef AUTH_MODE_CERT #define DYN_RESPONSE_BUFF_LEN (5 * 1024) #define DECODE_BUFF_LEN (5 * 1024) #else #define DYN_RESPONSE_BUFF_LEN (256) #define DECODE_BUFF_LEN (256) #endif /* Knuth's TAOCP section 3.6 */ #define M ((1U << 31) - 1) #define A 48271 #define Q 44488 // M/A #define R 3399 // M%A; R < Q !!! #define CODE_RESAULT "code" #define ENCRYPT_TYPE "encryptionType" #define PSK_DATA "psk" #define CERT_DATA "clientCert" #define KEY_DATA "clientKey" typedef enum { eCERT_TYPE = 1, ePSK_TYPE = 2, } eAuthType; /*Global value*/ static unsigned int _seed = 1; #ifndef AUTH_MODE_CERT static char *_get_json_psk(char *json) { char *psk = LITE_json_value_of(PSK_DATA, json); if (psk == NULL) { Log_e("Get psk fail: %s", json); } return psk; } #else static char *_get_json_cert_data(char *json) { char *cert = LITE_json_value_of(CERT_DATA, json); if (cert == NULL) { Log_e("Get clientCert fail: %s", json); } return cert; } static char *_get_json_key_data(char *json) { char *key = LITE_json_value_of(KEY_DATA, json); if (key == NULL) { Log_e("Get clientCert fail: %s", json); } return key; } /*\\n in data change to '\n'*/ static void _deal_transfer(char *data, uint32_t dataLen) { int i; for (i = 0; i < dataLen; i++) { if ((data[i] == '\\') && (data[i + 1] == 'n')) { data[i] = ' '; data[i + 1] = '\n'; } } } static int _cert_file_save(const char *fileName, char *data, uint32_t dataLen) { QFILE fp; char filePath[FILE_PATH_MAX_LEN]; uint32_t len; int Ret = QCLOUD_ERR_FAILURE; memset(filePath, 0, FILE_PATH_MAX_LEN); HAL_Snprintf(filePath, FILE_PATH_MAX_LEN, "./certs/%s", fileName); fp = ql_fopen(fileName, "w+"); if (fp<0) { Log_e("fail to open file %s", fileName); goto exit; } _deal_transfer(data, dataLen); len = ql_fwrite(data, dataLen, 1, fp); ql_fclose(fp); if (len == dataLen) { Log_d("save %s file succes", fileName); Ret = QCLOUD_RET_SUCCESS; } exit: return Ret; } #endif int rand_r(unsigned int *seed) { int32_t X; X = *seed; X = A * (X % Q) - R * (int32_t)(X / Q); if (X < 0) X += M; *seed = X; return X; } int rand_d(void) { return rand_r(&_seed); } void srand_d(unsigned int i) { _seed = i; } static int _get_json_result_code(char *json) { int32_t resault = -1; char *v = LITE_json_value_of(CODE_RESAULT, json); if (v == NULL) { Log_e("Invalid json content: %s", json); return -1; } if (LITE_get_int32(&resault, v) != QCLOUD_RET_SUCCESS) { Log_e("Invalid json content: %s", json); HAL_Free(v); return -1; } HAL_Free(v); return resault; } static int _get_json_encry_type(char *json) { int32_t type = -1; char *v = LITE_json_value_of(ENCRYPT_TYPE, json); if (v == NULL) { Log_e("Get encry type fail, %s", json); return -1; } if (LITE_get_int32(&type, v) != QCLOUD_RET_SUCCESS) { Log_e("Invalid json content: %s", json); HAL_Free(v); return -1; } HAL_Free(v); return type; } static int _parse_devinfo(char *jdoc, DeviceInfo *pDevInfo) { int ret = 0; size_t len; int datalen; int enType; unsigned int keybits; char key[UTILS_AES_BLOCK_LEN + 1]; char decodeBuff[DECODE_BUFF_LEN] = {0}; unsigned char iv[16]; char * payload = NULL; #ifdef AUTH_MODE_CERT char *clientCert; char *clientKey; #else char *psk; #endif Log_d("recv: %s", jdoc); ret = _get_json_result_code(jdoc); if (QCLOUD_RET_SUCCESS != ret) { Log_e("response err, ret:%d", ret); goto exit; } payload = LITE_json_value_of("payload", jdoc); if (payload == NULL) { Log_e("Invalid json content: %s", jdoc); ret = QCLOUD_ERR_FAILURE; goto exit; } else { Log_d("payload:%s", payload); } ret = qcloud_iot_utils_base64decode((uint8_t *)decodeBuff, sizeof(decodeBuff), &len, (uint8_t *)payload, strlen(payload)); if (ret != QCLOUD_RET_SUCCESS) { Log_e("Response decode err, response:%s", payload); ret = QCLOUD_ERR_FAILURE; goto exit; } datalen = len + (UTILS_AES_BLOCK_LEN - len % UTILS_AES_BLOCK_LEN); keybits = AES_KEY_BITS_128; memset(key, 0, UTILS_AES_BLOCK_LEN); strncpy(key, pDevInfo->product_secret, UTILS_AES_BLOCK_LEN); memset(iv, '0', UTILS_AES_BLOCK_LEN); ret = utils_aes_cbc((uint8_t *)decodeBuff, datalen, (uint8_t *)decodeBuff, DECODE_BUFF_LEN, UTILS_AES_DECRYPT, (uint8_t *)key, keybits, iv); if (QCLOUD_RET_SUCCESS == ret) { // Log_d("The decrypted data is:%s", decodeBuff); } else { Log_e("data decry err,ret:%d", ret); goto exit; } enType = _get_json_encry_type(decodeBuff); if (enType < 0) { Log_e("invlid encryt type, decrypt maybe faild"); ret = QCLOUD_ERR_FAILURE; goto exit; } #ifdef AUTH_MODE_CERT if (eCERT_TYPE != enType) { Log_e("encryt type should be cert type"); ret = QCLOUD_ERR_FAILURE; goto exit; } clientCert = _get_json_cert_data(decodeBuff); if (NULL != clientCert) { memset(pDevInfo->dev_cert_file_name, 0, MAX_SIZE_OF_DEVICE_CERT_FILE_NAME); HAL_Snprintf(pDevInfo->dev_cert_file_name, MAX_SIZE_OF_DEVICE_CERT_FILE_NAME, "%s_cert.crt", pDevInfo->device_name); if (QCLOUD_RET_SUCCESS != _cert_file_save(pDevInfo->dev_cert_file_name, clientCert, strlen(clientCert))) { Log_e("save %s file fail", pDevInfo->dev_cert_file_name); ret = QCLOUD_ERR_FAILURE; } HAL_Free(clientCert); } else { Log_e("Get clientCert data fail"); ret = QCLOUD_ERR_FAILURE; } clientKey = _get_json_key_data(decodeBuff); if (NULL != clientKey) { memset(pDevInfo->dev_key_file_name, 0, MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME); HAL_Snprintf(pDevInfo->dev_key_file_name, MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME, "%s_private.key", pDevInfo->device_name); if (QCLOUD_RET_SUCCESS != _cert_file_save(pDevInfo->dev_key_file_name, clientKey, strlen(clientKey))) { Log_e("save %s file fail", pDevInfo->dev_key_file_name); ret = QCLOUD_ERR_FAILURE; } HAL_Free(clientKey); } else { Log_e("Get clientCert data fail"); ret = QCLOUD_ERR_FAILURE; } #else if (ePSK_TYPE != enType) { Log_e("encryt type should be psk type"); ret = QCLOUD_ERR_FAILURE; goto exit; } psk = _get_json_psk(decodeBuff); if (NULL != psk) { if (strlen(psk) > MAX_SIZE_OF_DEVICE_SECRET) { Log_e("psk exceed max len,%s", psk); strcpy(pDevInfo->device_secret, psk); } else { strncpy(pDevInfo->device_secret, psk, MAX_SIZE_OF_DEVICE_SECRET); pDevInfo->device_secret[MAX_SIZE_OF_DEVICE_SECRET] = '\0'; } HAL_Free(psk); } else { Log_e("Get psk data fail"); } #endif exit: if (payload) { HAL_Free(payload); } return ret; } static int _post_reg_request_by_http(unsigned char profile_idx, char *request_buf, DeviceInfo *pDevInfo) { int Ret = 0; HTTPClient http_client; /* http client */ HTTPClientData http_data; /* http client data */ const char *url_format = "%s://%s/register/dev"; char url[REG_URL_MAX_LEN] = {0}; int port; const char *ca_crt = NULL; char respbuff[DYN_RESPONSE_BUFF_LEN]; /*format URL*/ #ifndef AUTH_WITH_NOTLS HAL_Snprintf(url, REG_URL_MAX_LEN, url_format, "https", iot_get_dyn_reg_domain(pDevInfo->region)); port = DYN_REG_SERVER_PORT_TLS; ca_crt = iot_ca_get(); #else HAL_Snprintf(url, REG_URL_MAX_LEN, url_format, "http", iot_get_dyn_reg_domain(pDevInfo->region)); port = DYN_REG_SERVER_PORT; #endif memset((char *)&http_client, 0, sizeof(HTTPClient)); memset((char *)&http_data, 0, sizeof(HTTPClientData)); http_client.header = "Accept: text/xml,application/json;*/*\r\n"; http_client.profile_idx = profile_idx; http_data.post_content_type = "application/x-www-form-urlencoded"; http_data.post_buf = request_buf; http_data.post_buf_len = strlen(request_buf); Ret = qcloud_http_client_common(&http_client, url, port, ca_crt, HTTP_POST, &http_data); if (QCLOUD_RET_SUCCESS != Ret) { Log_e("qcloud_http_client_common failed, Ret = %d", Ret); return Ret; } memset(respbuff, 0, DYN_RESPONSE_BUFF_LEN); http_data.response_buf_len = DYN_RESPONSE_BUFF_LEN; http_data.response_buf = respbuff; Ret = qcloud_http_recv_data(&http_client, DYN_REG_RES_HTTP_TIMEOUT_MS, &http_data); if (QCLOUD_RET_SUCCESS != Ret) { Log_e("dynamic register response fail, Ret = %d", Ret); } else { /*Parse dev info*/ Ret = _parse_devinfo(http_data.response_buf, pDevInfo); if (QCLOUD_RET_SUCCESS != Ret) { Log_e("parse device info err"); } } qcloud_http_client_close(&http_client); return Ret; } static int _cal_dynreg_sign(DeviceInfo *pDevInfo, char *signout, int max_signlen, int nonce, uint32_t timestamp) { int sign_len; size_t olen = 0; char * pSignSource = NULL; const char *sign_fmt = "deviceName=%s&nonce=%d&productId=%s×tamp=%d"; char sign[DYN_REG_SIGN_LEN] = {0}; /*format sign data*/ sign_len = strlen(sign_fmt) + strlen(pDevInfo->device_name) + strlen(pDevInfo->product_id) + sizeof(int) + sizeof(uint32_t) + DYN_BUFF_DATA_MORE; pSignSource = HAL_Malloc(sign_len); if (pSignSource == NULL) { Log_e("malloc sign source buff fail"); return QCLOUD_ERR_FAILURE; } memset(pSignSource, 0, sign_len); HAL_Snprintf((char *)pSignSource, sign_len, sign_fmt, pDevInfo->device_name, nonce, pDevInfo->product_id, timestamp); /*cal hmac sha1*/ utils_hmac_sha1(pSignSource, strlen(pSignSource), sign, pDevInfo->product_secret, strlen(pDevInfo->product_secret)); /*base64 encode*/ qcloud_iot_utils_base64encode((uint8_t *)signout, max_signlen, &olen, (const uint8_t *)sign, strlen(sign)); HAL_Free(pSignSource); return (olen > max_signlen) ? QCLOUD_ERR_FAILURE : QCLOUD_RET_SUCCESS; } int IOT_DynReg_Device(unsigned char profile_idx, DeviceInfo *pDevInfo) { const char *para_format = "{\"deviceName\":\"%s\",\"nonce\":%d,\"productId\":" "\"%s\",\"timestamp\":%d,\"signature\":\"%s\"}"; int nonce; int Ret; uint32_t timestamp; int len; char sign[DYN_REG_SIGN_LEN] = {0}; char * pRequest = NULL; if (strlen(pDevInfo->product_secret) < UTILS_AES_BLOCK_LEN) { Log_e("product key inllegal"); return QCLOUD_ERR_FAILURE; } srand_d(HAL_GetTimeMs()); nonce = rand_d(); timestamp = HAL_GetTimeMs(); /*cal sign*/ if (QCLOUD_RET_SUCCESS == _cal_dynreg_sign(pDevInfo, sign, DYN_REG_SIGN_LEN, nonce, timestamp)) { Log_d("sign:%s", sign); } else { Log_e("cal sign fail"); return QCLOUD_ERR_FAILURE; } /*format http request*/ len = strlen(para_format) + strlen(pDevInfo->product_id) + strlen(pDevInfo->device_name) + sizeof(int) + sizeof(uint32_t) + strlen(sign) + DYN_BUFF_DATA_MORE; pRequest = HAL_Malloc(len); if (!pRequest) { Log_e("malloc request memory fail"); return QCLOUD_ERR_FAILURE; } memset(pRequest, 0, len); HAL_Snprintf(pRequest, len, para_format, pDevInfo->device_name, nonce, pDevInfo->product_id, timestamp, sign); Log_d("request:%s", pRequest); Log_d("resbuff len:%d", DYN_RESPONSE_BUFF_LEN); /*post request*/ Ret = _post_reg_request_by_http(profile_idx, pRequest, pDevInfo); if (QCLOUD_RET_SUCCESS == Ret) { Log_d("request dev info success"); } else { Log_e("request dev info fail"); } HAL_Free(pRequest); return Ret; } #ifdef __cplusplus } #endif