// Copyright (2025) Beijing Volcano Engine Technology Ltd. // SPDX-License-Identifier: Apache-2.0 #include "base/volc_device_manager.h" #include "util/volc_log.h" #include "volc_platform.h" #include "volc_http.h" #include #include #include #include #include // For HMAC-SHA256 signature generation #include #include #include // AES解密辅助函数 static char* volc_aes_decode_internal(const char* secret, const char* input, const bool partial_secret) { size_t decoded_len = 0; int key_bits = 0; char* key = NULL; unsigned char iv[16] = {0}; // 使用全零IV,与官方项目保持一致 unsigned char* decoded_payload = NULL; unsigned char* output = NULL; if (input == NULL || strlen(input) == 0) { LOGE("Input is NULL or empty"); return NULL; } // 计算Base64解码后的长度(与官方项目保持一致) int padding = 0; int input_len = strlen(input); if (input_len >= 2 && input[input_len - 1] == '=' && input[input_len - 2] == '=') { padding = 2; } else if (input[input_len - 1] == '=') { padding = 1; } decoded_len = (input_len * 3) / 4 - padding; // 分配内存用于存储解码后的payload decoded_payload = (unsigned char*)hal_calloc(decoded_len + 1, 1); if (decoded_payload == NULL) { LOGE("Failed to allocate memory for decoded payload"); return NULL; } // 执行Base64解码(与官方项目保持一致的错误处理) size_t actual_len = 0; int ret = mbedtls_base64_decode(decoded_payload, decoded_len + 1, &actual_len, (const unsigned char*)input, input_len); if (ret != 0) { LOGW("Base64 decode failed with error: %d, but continuing", ret); // 即使解码失败,也继续执行,与官方项目保持一致 } else { decoded_len = actual_len; } // 准备AES密钥和IV(与官方项目保持一致) if (partial_secret) { // 使用128位密钥 key = (char*)hal_calloc(16, 1); if (key == NULL) { LOGE("Failed to allocate memory for key"); hal_free(decoded_payload); return NULL; } memcpy(key, secret, 16); key_bits = 128; } else { // 使用192位密钥 size_t secret_len = strlen(secret); if (secret_len < 24) { LOGE("Secret length is less than 24, secret: %s", secret); hal_free(decoded_payload); return NULL; } key = (char*)hal_calloc(24, 1); if (key == NULL) { LOGE("Failed to allocate memory for key"); hal_free(decoded_payload); return NULL; } memcpy(key, secret, 24); key_bits = 192; } // 分配内存用于存储解密后的结果(与官方项目保持一致的内存分配) output = (unsigned char*)hal_calloc(decoded_len + 16, 1); if (output == NULL) { LOGE("Failed to allocate memory for output"); hal_free(key); hal_free(decoded_payload); return NULL; } // 使用密钥的前16字节作为IV(与官方项目保持一致) memcpy(iv, secret, 16); // 执行AES解密 - 使用CBC模式(与官方项目保持一致) mbedtls_aes_context aes_ctx; mbedtls_aes_init(&aes_ctx); mbedtls_aes_setkey_dec(&aes_ctx, (const unsigned char*)key, key_bits); // 使用CBC模式解密 ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, decoded_len, iv, decoded_payload, output); mbedtls_aes_free(&aes_ctx); // 清理临时变量 hal_free(key); hal_free(decoded_payload); if (ret != 0) { LOGE("Failed to decrypt payload: %d", ret); hal_free(output); return NULL; } // 去除解密后可能存在的空白字符(与官方项目保持一致) int len = strlen((const char*)output); while (len > 0 && (output[len-1] < 32 || output[len-1] == 0x20)) { --len; } output[len] = '\0'; return (char*)output; } // 定义常量 #define VOLC_IOT_HOST "https://iot-cn-shanghai.iot.volces.com" #define VOLC_DYNAMIC_REGISTER_PATH "/2021-12-14/DynamicRegister" #define VOLC_API_VERSION "2021-12-14" #define VOLC_API_VERSION_QUERY_PARAM "Version=2021-12-14" #define VOLC_API_ACTION_DYNAMIC_REGISTER "Action=DynamicRegister" #define VOLC_GET_RTC_CONFIG_PATH "/2021-12-14/GetRTCConfig" #define VOLC_API_ACTION_GET_RTC_CONFIG "Action=GetRTCConfig" // 错误码映射函数(暂时简化实现) int volc_inter_err_2_ext_err(int code) { switch (code) { case ERROR_LICENSE_EXHAUSTED: return -1; // VOLC_ERR_LICENSE_EXHAUSTED case ERROR_LICENSE_EXPIRED: return -2; // VOLC_ERR_LICENSE_EXPIRED default: return -3; // VOLC_ERR_FAILED } } // 生成签名函数 - 使用HMAC-SHA256算法 char* volc_generate_signature(const char* secret_key, const char* product_key, const char* device_name, int rnd, uint64_t timestamp, int auth_type) { if (!secret_key || !product_key || !device_name) { LOGE("Invalid parameters for signature generation"); return NULL; } // 构造待签名字符串(与官方项目格式一致,确保参数顺序正确) char sign_str[512] = {0}; int sign_str_len = snprintf(sign_str, sizeof(sign_str), "auth_type=%d&device_name=%s&random_num=%d&product_key=%s×tamp=%" PRIu64, auth_type, device_name, rnd, product_key, timestamp); if (sign_str_len <= 0 || sign_str_len >= sizeof(sign_str)) { LOGE("Failed to construct sign string"); return NULL; } // 计算HMAC-SHA256哈希值 unsigned char hmac_result[32] = {0}; mbedtls_md_context_t ctx; mbedtls_md_init(&ctx); int ret = mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); if (ret != 0) { LOGE("Failed to setup MD context: %d", ret); mbedtls_md_free(&ctx); return NULL; } ret = mbedtls_md_hmac_starts(&ctx, (const unsigned char*)secret_key, strlen(secret_key)); if (ret != 0) { LOGE("Failed to start HMAC: %d", ret); mbedtls_md_free(&ctx); return NULL; } ret = mbedtls_md_hmac_update(&ctx, (const unsigned char*)sign_str, sign_str_len); if (ret != 0) { LOGE("Failed to update HMAC: %d", ret); mbedtls_md_free(&ctx); return NULL; } ret = mbedtls_md_hmac_finish(&ctx, hmac_result); if (ret != 0) { LOGE("Failed to finish HMAC: %d", ret); mbedtls_md_free(&ctx); return NULL; } mbedtls_md_free(&ctx); // 将HMAC结果进行Base64编码 // 使用更可靠的Base64长度计算方式 size_t src_len = sizeof(hmac_result); size_t base64_len = ((src_len + 2) / 3) * 4 + 1; // +1 for null terminator char* signature = (char*)hal_malloc(base64_len); if (!signature) { LOGE("Failed to allocate signature buffer"); return NULL; } size_t olen = 0; int ret_b64 = mbedtls_base64_encode((unsigned char*)signature, base64_len, &olen, hmac_result, src_len); if (ret_b64 != 0) { LOGE("Failed to encode Base64: %d", ret_b64); hal_free(signature); return NULL; } signature[olen] = '\0'; return signature; } // 生成WebSocket签名函数(与官方项目实现一致) char* volc_generate_signature_ws(const char* secret_key, const char* product_key, const char* device_name, const char* instance_id, int rnd, uint64_t timestamp, int auth_type) { if (!secret_key || !product_key || !device_name || !instance_id) { LOGE("Invalid parameters for WebSocket signature generation"); return NULL; } // 构造待签名字符串(与官方项目格式一致) char sign_str[512] = {0}; int sign_str_len = snprintf(sign_str, sizeof(sign_str), "auth_type=%d&device_name=%s&random_num=%d&product_key=%s×tamp=%" PRIu64 "&instance_id=%s", auth_type, device_name, rnd, product_key, timestamp, instance_id); if (sign_str_len <= 0 || sign_str_len >= sizeof(sign_str)) { LOGE("Failed to construct WebSocket sign string"); return NULL; } // 计算HMAC-SHA256哈希值 unsigned char hmac_result[32] = {0}; mbedtls_md_context_t ctx; mbedtls_md_init(&ctx); int ret = mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); if (ret != 0) { LOGE("Failed to setup MD context for WebSocket signature: %d", ret); mbedtls_md_free(&ctx); return NULL; } ret = mbedtls_md_hmac_starts(&ctx, (const unsigned char*)secret_key, strlen(secret_key)); if (ret != 0) { LOGE("Failed to start HMAC for WebSocket signature: %d", ret); mbedtls_md_free(&ctx); return NULL; } ret = mbedtls_md_hmac_update(&ctx, (const unsigned char*)sign_str, sign_str_len); if (ret != 0) { LOGE("Failed to update HMAC for WebSocket signature: %d", ret); mbedtls_md_free(&ctx); return NULL; } ret = mbedtls_md_hmac_finish(&ctx, hmac_result); if (ret != 0) { LOGE("Failed to finish HMAC for WebSocket signature: %d", ret); mbedtls_md_free(&ctx); return NULL; } mbedtls_md_free(&ctx); // 将HMAC结果进行Base64编码 size_t src_len = sizeof(hmac_result); size_t base64_len = ((src_len + 2) / 3) * 4 + 1; // +1 for null terminator char* signature = (char*)hal_malloc(base64_len); if (!signature) { LOGE("Failed to allocate WebSocket signature buffer"); return NULL; } size_t olen = 0; int ret_b64 = mbedtls_base64_encode((unsigned char*)signature, base64_len, &olen, hmac_result, src_len); if (ret_b64 != 0) { LOGE("Failed to encode WebSocket signature Base64: %d", ret_b64); hal_free(signature); return NULL; } signature[olen] = '\0'; return signature; } // 实现设备注册函数 int volc_device_register(volc_iot_info_t* info, char** output) { if (!info || !output || !info->product_key || !info->device_name || !info->product_secret) { LOGE("Invalid input parameters: info=%p, output=%p, product_key=%p, device_name=%p, product_secret=%p", info, output, info->product_key, info->device_name, info->product_secret); return -1; } int ret = 0; uint64_t current_time = hal_get_time_ms(); int32_t random_num = (int32_t)current_time; char url[256] = {0}; char* signature = NULL; char* response = NULL; cJSON* root = NULL; cJSON* rtc_app_id = NULL; char* device_secret_str = NULL; // 使用正确的变量名,与解密后的变量保持一致 char* json_str = NULL; // 初始化json_str变量,避免未初始化警告 cJSON* request_root = NULL; // 生成签名(使用正确的auth_type值1,与官方项目保持一致) signature = volc_generate_signature(info->product_secret, info->product_key, info->device_name, random_num, current_time, 1); if (!signature) { LOGE("Failed to generate signature"); return -1; } // 构造URL snprintf(url, sizeof(url), "%s%s?%s&%s", VOLC_IOT_HOST, VOLC_DYNAMIC_REGISTER_PATH, VOLC_API_ACTION_DYNAMIC_REGISTER, VOLC_API_VERSION_QUERY_PARAM); LOGI("Device register URL: %s", url); // 使用cJSON构造请求体(与官方实现保持一致,避免字符串长度限制问题) request_root = cJSON_CreateObject(); if (!request_root) { LOGE("Failed to create JSON root object for request"); ret = -1; goto err_out_label; } // 总是添加InstanceID字段 cJSON_AddStringToObject(request_root, "InstanceID", info->instance_id);// 必须添加 实例ID字段 cJSON_AddStringToObject(request_root, "product_key", info->product_key);// 必须添加 产品密钥字段 cJSON_AddStringToObject(request_root, "device_name", info->device_name);// 必须添加 设备名称字段 // cJSON_AddStringToObject(request_root, "bot_id", info->bot_id); cJSON_AddNumberToObject(request_root, "random_num", random_num);// 必须添加 随机数字段 cJSON_AddNumberToObject(request_root, "timestamp", (double)current_time); // 与官方实现保持一致,使用double类型 cJSON_AddNumberToObject(request_root, "auth_type", 1);// 必须添加 认证类型字段 cJSON_AddStringToObject(request_root, "signature", signature);// 必须添加 签名字段 json_str = cJSON_PrintUnformatted(request_root);// 必须添加 JSON字符串字段 if (!json_str) { LOGE("Failed to print JSON request body"); cJSON_Delete(request_root); ret = -1; goto err_out_label; } // 调用HTTP POST请求 response = volc_http_post(url, json_str, strlen(json_str)); if (!response) { LOGE("Failed to send HTTP POST request for device registration"); cJSON_Delete(request_root); hal_free(json_str); ret = -1; goto err_out_label; } LOGI("Device register response received: %s", response); // 解析JSON响应 root = cJSON_Parse(response); if (!root) { LOGE("Failed to parse device registration response"); ret = -1; goto err_out_label; } // 检查是否有错误信息(参考官方实现的检查方式) cJSON* response_metadata = cJSON_GetObjectItem(root, "ResponseMetadata"); if (response_metadata && response_metadata->type == cJSON_Object) { cJSON* error = cJSON_GetObjectItem(response_metadata, "Error"); if (error && error->type == cJSON_Object) { cJSON* code_n = cJSON_GetObjectItem(error, "CodeN"); if (code_n && code_n->type == cJSON_Number) { // 检查是否是RTC许可证绑定失败错误 int error_code = (int)code_n->valuedouble; cJSON* message = cJSON_GetObjectItem(error, "Message"); LOGE("Device registration failed, error code: %d", error_code); if (message && message->type == cJSON_String && message->valuestring) { LOGE("Error message: %s", message->valuestring); } // 如果是RTC许可证绑定失败,记录更详细的信息 if (error_code == 12000130) { LOGE("Device bind RTC license failed, please check device configuration"); } ret = -1; goto err_out_label; } } } // 检查响应格式(兼容官方项目的ResponseMetadata/Result格式) cJSON* result = cJSON_GetObjectItem(root, "Result"); if (!result || result->type != cJSON_Object) { LOGE("Device registration response missing or invalid 'Result' field"); ret = -1; goto err_out_label; } // 提取rtc_app_id(安全检查) rtc_app_id = cJSON_GetObjectItem(result, "RTCAppID"); if (!rtc_app_id || rtc_app_id->type != cJSON_String || !rtc_app_id->valuestring || strlen(rtc_app_id->valuestring) == 0) { LOGE("Device registration response missing or invalid 'RTCAppID' field"); ret = -1; goto err_out_label; } // 从payload中解析device_secret(安全检查) cJSON* payload = cJSON_GetObjectItem(result, "payload"); if (!payload || payload->type != cJSON_String || !payload->valuestring || strlen(payload->valuestring) == 0) { LOGE("Device registration response missing or invalid 'payload' field"); ret = -1; goto err_out_label; } // 使用AES解密payload获取device_secret device_secret_str = volc_aes_decode_internal(info->product_secret, payload->valuestring, true); if (!device_secret_str) { LOGE("Failed to decode device secret from payload"); ret = -1; goto err_out_label; } // 验证解密结果是否有效 if (strlen(device_secret_str) == 0) { LOGE("Decrypted device secret is empty"); hal_free(device_secret_str); device_secret_str = NULL; ret = -1; goto err_out_label; } LOGI("Decoded device secret: %s", device_secret_str); // 存储device_secret到iot_info结构体中 info->device_secret = strdup(device_secret_str); if (!info->device_secret) { LOGE("Failed to allocate memory for device secret"); hal_free(device_secret_str); ret = -1; goto err_out_label; } // 返回device_secret if (*output) { hal_free(*output); *output = NULL; } // 使用解密后的device_secret_str赋值给output *output = strdup(device_secret_str); if (!*output) { LOGE("Failed to allocate memory for device secret"); hal_free(device_secret_str); ret = -1; goto err_out_label; } // 释放临时内存 hal_free(device_secret_str); device_secret_str = NULL; // 保存rtc_app_id if (info->rtc_app_id) { hal_free(info->rtc_app_id); info->rtc_app_id = NULL; } info->rtc_app_id = strdup(rtc_app_id->valuestring); if (!info->rtc_app_id) { LOGE("Failed to allocate memory for rtc_app_id"); ret = -1; goto err_out_label; } LOGI("Device registration successful: rtc_app_id=%s", info->rtc_app_id); err_out_label: // 清理资源 if (root) { cJSON_Delete(root); } if (request_root) { cJSON_Delete(request_root); } if (json_str) { hal_free(json_str); } if (response) { hal_free(response); } if (signature) { hal_free(signature); } if (device_secret_str) { hal_free(device_secret_str); } return ret; } // 实现RTC配置获取函数 ,extra_params 用于传递额外的AgentConfig配置参数 int volc_get_rtc_config(volc_iot_info_t* info, int audio_codec, const char* bot_id, const char* task_id, const char* extra_params, volc_room_info_t* room_info) { if (!info || !room_info || !bot_id || !task_id || !info->product_key || !info->device_name || !info->device_secret) { LOGE("Invalid input parameters: info=%p, room_info=%p, bot_id=%p, task_id=%p, product_key=%p, device_name=%p, device_secret=%p", info, room_info, bot_id, task_id, info->product_key, info->device_name, info->device_secret); return -1; } int ret = 0; uint64_t current_time = hal_get_time_ms(); int32_t random_num = (int32_t)current_time; char url[256] = {0}; char* signature = NULL; char* response = NULL; cJSON* root = NULL; cJSON* channel_name = NULL; cJSON* uid = NULL; cJSON* token = NULL; cJSON* request_root = NULL; char* json_str = NULL; // 生成签名(使用auth_type=0,与官方代码一致) signature = volc_generate_signature(info->device_secret, info->product_key, info->device_name, random_num, current_time, 0); if (!signature) { LOGE("Failed to generate signature"); return -1; } // 构造URL snprintf(url, sizeof(url), "%s%s?%s&%s", VOLC_IOT_HOST, VOLC_GET_RTC_CONFIG_PATH, VOLC_API_ACTION_GET_RTC_CONFIG, VOLC_API_VERSION_QUERY_PARAM); // 使用cJSON构造JSON请求体(与官方代码保持一致) request_root = cJSON_CreateObject(); cJSON_AddStringToObject(request_root, "InstanceID", info->instance_id); cJSON_AddStringToObject(request_root, "product_key", info->product_key); cJSON_AddStringToObject(request_root, "device_name", info->device_name); cJSON_AddNumberToObject(request_root, "random_num", random_num); cJSON_AddNumberToObject(request_root, "timestamp", (double)current_time); cJSON_AddStringToObject(request_root, "signature", signature); cJSON_AddStringToObject(request_root, "bot_id", bot_id); cJSON_AddNumberToObject(request_root, "audio_codec", audio_codec); cJSON_AddStringToObject(request_root, "task_id", task_id); // 如果有额外参数(如AgentConfig),解析并合并到请求体中 if (extra_params && strlen(extra_params) > 0) { cJSON* params_json = cJSON_Parse(extra_params); if (params_json) { // 如果是对象,遍历所有字段并添加到request_root if (cJSON_IsObject(params_json)) { cJSON* child = params_json->child; while (child) { cJSON_AddItemToObject(request_root, child->string, cJSON_Duplicate(child, 1)); child = child->next; } } else { // 如果不是对象(不太可能),尝试直接添加为AgentConfig(备选方案) LOGW("extra_params is not a JSON object, ignoring"); } cJSON_Delete(params_json); } else { LOGW("Failed to parse extra_params JSON"); } } json_str = cJSON_PrintUnformatted(request_root); LOGI("Get RTC config URL: %s", url); LOGI("RTC config request body: %s", json_str); // 调用HTTP POST请求 response = volc_http_post(url, json_str, strlen(json_str)); if (!response) { LOGE("Failed to send HTTP POST request for RTC config"); ret = -1; goto err_out_label; } LOGI("Get RTC config response received"); LOGI("RTC config response content: %s", response); // 解析JSON响应 root = cJSON_Parse(response); if (!root) { LOGE("Failed to parse RTC config response: %s", response); ret = -1; goto err_out_label; } // 检查响应格式(兼容官方项目的ResponseMetadata/Result格式) cJSON* result = cJSON_GetObjectItem(root, "Result"); if (!result || result->type != cJSON_Object) { LOGE("RTC config response missing or invalid 'Result' field"); ret = -1; goto err_out_label; } // 提取RTC配置信息(与官方代码保持一致的字段名) channel_name = cJSON_GetObjectItem(result, "RoomID"); if (!channel_name || channel_name->type != cJSON_String || !channel_name->valuestring) { LOGE("RTC config response missing or invalid 'RoomID' field"); ret = -1; goto err_out_label; } uid = cJSON_GetObjectItem(result, "UserID"); if (!uid || uid->type != cJSON_String || !uid->valuestring) { LOGE("RTC config response missing or invalid 'UserID' field"); ret = -1; goto err_out_label; } token = cJSON_GetObjectItem(result, "Token"); if (!token || token->type != cJSON_String || !token->valuestring) { LOGE("RTC config response missing or invalid 'Token' field"); ret = -1; goto err_out_label; } // 提取TaskID(与官方代码保持一致) cJSON* task_id_json = cJSON_GetObjectItem(result, "TaskID"); if (!task_id_json || task_id_json->type != cJSON_String || !task_id_json->valuestring) { LOGE("RTC config response missing or invalid 'TaskID' field"); ret = -1; goto err_out_label; } // 保存RTC配置(先释放旧的内存) char* new_channel_name = strdup(channel_name->valuestring); char* new_uid = strdup(uid->valuestring); char* new_token = strdup(token->valuestring); char* new_task_id = strdup(task_id_json->valuestring); // 检查内存分配是否成功 if (!new_channel_name || !new_uid || !new_token || !new_task_id) { LOGE("Failed to allocate memory for RTC config"); ret = -1; // 清理已分配的内存 if (new_channel_name) hal_free(new_channel_name); if (new_uid) hal_free(new_uid); if (new_token) hal_free(new_token); if (new_task_id) hal_free(new_task_id); goto err_out_label; } // 释放旧的内存并设置新的指针 if (room_info->rtc_opt.p_channel_name) { hal_free(room_info->rtc_opt.p_channel_name); } if (room_info->rtc_opt.p_uid) { hal_free(room_info->rtc_opt.p_uid); } if (room_info->rtc_opt.p_token) { hal_free(room_info->rtc_opt.p_token); } if (room_info->task_id) { hal_free(room_info->task_id); } // 保存RTC配置 room_info->rtc_opt.p_channel_name = new_channel_name; room_info->rtc_opt.p_uid = new_uid; room_info->rtc_opt.p_token = new_token; room_info->task_id = new_task_id; // 检查是否所有分配都成功 if (!room_info->rtc_opt.p_channel_name || !room_info->rtc_opt.p_uid || !room_info->rtc_opt.p_token || !room_info->task_id) { LOGE("Failed to allocate memory for RTC config"); ret = -1; goto err_out_label; } LOGI("Retrieved RTC config: channel_name=%s, uid=%s, task_id=%s", room_info->rtc_opt.p_channel_name, room_info->rtc_opt.p_uid, room_info->task_id); err_out_label: // 清理资源 if (root) { cJSON_Delete(root); } if (request_root) { cJSON_Delete(request_root); } if (json_str) { hal_free(json_str); } if (response) { hal_free(response); } if (signature) { hal_free(signature); } return ret; }