1. 项目概述

cube_crypto 是一个面向嵌入式微控制器(尤其是 STM32 系列)深度优化的轻量级密码学支持库,其设计目标并非替代 OpenSSL 或 Mbed TLS 等全功能密码栈,而是为资源受限环境提供可裁剪、可验证、低耦合的底层密码原语实现与硬件加速桥接能力。该库名称中的 “cube” 明确指向 STMicroelectronics 的 STM32Cube 生态体系,表明其与 STM32CubeMX 配置工具、HAL/LL 驱动层及 STM32 系列专用加密外设(如 CRYP、HASH、RNG、PKA)存在原生集成关系;“crypto” 则直指其核心职能——提供密码学基础服务。

从工程实践角度看, cube_crypto 并非一个独立运行的“加密库”,而是一个 密码学能力抽象中间件 。它向上为应用层(如 TLS 协议栈、安全启动模块、固件签名验证器)提供统一、稳定的 C 接口;向下则智能适配不同芯片平台的能力谱系:在无硬件加密引擎的低端 MCU(如 STM32F0/F1)上,自动回退至经过 ARM Cortex-M 指令集(特别是 Thumb-2 和 DSP 扩展)深度优化的纯软件实现;在中高端型号(如 STM32F4/F7/H7/L4+/U5)上,则无缝接管 CRYP(AES/DES/TDES)、HASH(SHA-1/SHA-224/SHA-256)、RNG(真随机数生成)及 PKA(公钥加速器,用于 ECC/RSA 运算)等专用外设,显著降低 CPU 占用率并提升吞吐量。

该库的典型部署场景包括但不限于:

  • 安全固件空中升级(FOTA)中的镜像完整性校验(SHA-256)与签名验证(ECDSA with secp256r1);
  • 蓝牙 LE Secure Connections 或 Zigbee PRO 中的链路层密钥派生(ECDH + HKDF);
  • 工业网关设备中 TLS 1.2/1.3 握手阶段的 RSA/ECC 密钥交换与证书验证;
  • 基于硬件 RNG 的密钥材料生成,规避软件 PRNG 的熵源不足风险;
  • 安全启动流程中对 Flash 中 Bootloader 和 Application 分区的 AES-GCM 加密解密与认证。

其价值不在于算法数量的堆砌,而在于将密码学从“理论正确”推向“工程可靠”的关键一环:通过 HAL 层标准化接口屏蔽硬件差异,通过编译时配置( #define )实现零运行时开销的功能裁剪,通过严格遵循 FIPS 180-4 / FIPS 197 / NIST SP 800-90A 等标准的实现确保算法合规性,并通过与 STM32CubeIDE 工程模板的深度绑定,使开发者能在图形化配置界面中一键启用/禁用特定算法模块及其硬件加速路径。

2. 核心架构与模块划分

cube_crypto 采用分层架构设计,严格遵循关注点分离原则,各模块间通过明确定义的头文件接口进行通信,无隐式依赖。整个架构自底向上分为四层:

2.1 硬件抽象层(HAL)

此层是 cube_crypto 与物理芯片的唯一接触面,完全复用 STM32Cube HAL 库提供的标准外设驱动。它不包含任何密码逻辑,仅负责:

  • 初始化加密外设时钟与 GPIO(如 CRYP 外设的时钟使能、HASH 的输入引脚复用配置);
  • 调用 HAL_CRYP_Init() / HAL_HASH_Init() / HAL_RNG_Init() 等标准初始化函数;
  • 封装外设寄存器访问为线程安全的 HAL API 调用(例如 HAL_CRYP_Encrypt() 替代直接操作 CRYP->CR CRYP->DR );
  • 处理外设中断或 DMA 传输完成回调,将底层事件转换为上层可消费的状态信号。

关键设计考量在于: 所有 HAL 调用均被封装在条件编译块内 。当用户在 cube_crypto_conf.h 中定义 CUBE_CRYPTO_USE_HW_CRYP 时,相关函数才启用硬件路径;否则,编译器将剔除全部 CRYP 相关代码,链接器自动选择软件实现版本。这种设计确保了库的二进制尺寸与目标平台能力严格匹配。

2.2 算法实现层(Core)

这是库的核心逻辑所在,包含所有密码原语的 C 语言实现。每个算法均以独立 .c/.h 文件组织,命名规范为 crypto_<algorithm>.c (如 crypto_aes.c , crypto_sha256.c , crypto_ecp.c )。其实现遵循以下工程准则:

  • 常量时间(Constant-Time)编码 :所有分支( if/else )和内存访问索引均不依赖于敏感数据(如密钥字节),彻底消除基于时序或缓存侧信道攻击的风险。例如,在 AES 的 S-Box 查表操作中,使用位运算组合掩码而非直接数组索引。
  • 内存安全 :所有缓冲区操作均进行显式长度检查,杜绝缓冲区溢出。关键结构体(如 crypto_aes_context_t )内部包含 size_t ctx_size 字段,供运行时断言验证。
  • 零全局状态 :所有函数均为纯函数(Pure Function)或接受完整上下文结构体指针作为参数,避免使用 static 变量,确保多线程/RTOS 环境下的可重入性。

crypto_aes.c 为例,其暴露的主要 API 如下:

函数签名 功能说明 关键参数解析
int32_t crypto_aes_setkey_enc(crypto_aes_context_t *ctx, const uint8_t *key, uint32_t keybits) 设置 AES 加密密钥 keybits : 必须为 128/192/256,决定轮数(10/12/14); ctx 内部预计算并存储轮密钥(Round Keys)
int32_t crypto_aes_crypt_ecb(crypto_aes_context_t *ctx, int32_t mode, const uint8_t *input, uint8_t *output) ECB 模式加解密 mode : CRYPTO_AES_ENCRYPT CRYPTO_AES_DECRYPT input/output 必须为 16 字节对齐且长度为 16 的整数倍
int32_t crypto_aes_crypt_cbc(crypto_aes_context_t *ctx, int32_t mode, uint32_t length, uint8_t *iv, const uint8_t *input, uint8_t *output) CBC 模式加解密 iv : 初始化向量,长度 16 字节; length : 数据总长度(字节),必须为 16 的整数倍

2.3 服务封装层(Service)

此层将底层算法组合为更高阶的安全服务,解决实际工程问题。它不引入新算法,而是提供符合行业标准的协议级封装。主要服务包括:

  • 密钥派生函数(KDF) :实现 PBKDF2-HMAC-SHA256(用于口令派生密钥)和 HKDF-Expand/Extract(用于从 ECDH 共享密钥派生会话密钥)。接口示例如下:

    typedef struct {
        const uint8_t *salt;   // 盐值
        size_t salt_len;
        const uint8_t *info;   // 应用特定信息
        size_t info_len;
        size_t key_len;        // 输出密钥长度(字节)
    } crypto_hkdf_params_t;
    
    int32_t crypto_hkdf_expand(const uint8_t *prk, size_t prk_len,
                                const crypto_hkdf_params_t *params,
                                uint8_t *okm);
    
  • 数字签名/验证 :针对 ECDSA,提供 crypto_ecdsa_sign() crypto_ecdsa_verify() ,内部自动调用 crypto_ecp (椭圆曲线点运算)与 crypto_sha256 (消息摘要)。要求私钥以标准 SEC1 格式( 0x04 || X || Y )传入,公钥坐标经压缩( 0x02/0x03 || X )或未压缩格式均可处理。

  • 认证加密(AEAD) :提供 AES-GCM 模式实现,同时输出密文与认证标签(Tag)。其 crypto_gcm_crypt_and_tag() 函数需指定 tag_len (通常 12 或 16 字节),并在内部严格校验 GCM 认证失败时返回错误码 CRYPTO_ERR_GCM_AUTH_FAILED ,而非静默继续。

2.4 配置与集成层(Config & Integration)

位于顶层,负责库的整体行为定制与生态集成。核心文件为 cube_crypto_conf.h ,其关键配置项如下表所示:

配置宏 默认值 作用说明 工程影响
CUBE_CRYPTO_CONFIG_FILE "cube_crypto_conf.h" 允许用户重定义配置文件路径,便于多项目共享配置 编译时通过 -DCUBE_CRYPTO_CONFIG_FILE="my_conf.h" 覆盖
CUBE_CRYPTO_USE_HW_CRYP 0 启用 CRYP 硬件加速(需芯片支持) 若设为 1 ,则 crypto_aes_crypt_ecb() 等函数内部调用 HAL_CRYP_Encrypt() ,否则调用软件实现
CUBE_CRYPTO_USE_HW_HASH 0 启用 HASH 硬件加速 影响 crypto_sha256_starts() 等函数的底层路径选择
CUBE_CRYPTO_ECP_MAX_BITS 256 设置 ECC 运算最大位宽(决定 RAM 占用) 256 对应 secp256r1, 384 对应 secp384r1;增大此值显著增加 crypto_ecp_group 结构体大小
CUBE_CRYPTO_HAVE_AESNI 0 (仅 x86 仿真环境)启用 AES-NI 指令加速 嵌入式开发中通常保持 0

此外,该层提供与 FreeRTOS 的显式集成头文件 cube_crypto_freertos.h ,其中定义了:

  • crypto_mutex_t 类型,封装 SemaphoreHandle_t
  • crypto_mutex_init() / crypto_mutex_free() 创建/销毁互斥锁;
  • crypto_mutex_lock() / crypto_mutex_unlock() 在调用可能共享硬件资源的函数(如 HAL_HASH_Accumulate() )前加锁,防止多任务并发冲突。

3. 关键算法实现深度解析

3.1 AES 算法:软硬协同的性能平衡

cube_crypto 的 AES 实现在软件与硬件路径间展现出精妙的工程权衡。软件实现采用经典的“T-tables”查表法(4 个 256 项 uint32_t 表),在 Cortex-M4/M7 上达到约 12 cycles/byte 的吞吐率,远超朴素的“SubBytes+ShiftRows+MixColumns”逐字节计算。其核心循环代码片段如下(简化示意):

// crypto_aes.c 内部函数
static void aes_encrypt_block(const uint32_t *rk, const uint8_t in[16], uint8_t out[16]) {
    uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
    // 加载明文到状态寄存器
    GET_UINT32_LE(s0, in,  0); GET_UINT32_LE(s1, in,  4);
    GET_UINT32_LE(s2, in,  8); GET_UINT32_LE(s3, in, 12);

    // 初始轮密钥加
    s0 ^= rk[0]; s1 ^= rk[1]; s2 ^= rk[2]; s3 ^= rk[3];

    // 主轮循环 (10轮 for AES-128)
    for (int i = 0; i < 10; i++) {
        // T-tables 查表:一次查表完成 SubBytes+ShiftRows+MixColumns
        t0 = T0[(s0      ) & 0xFF] ^
             T1[(s1 >>  8) & 0xFF] ^
             T2[(s2 >> 16) & 0xFF] ^
             T3[(s3 >> 24) & 0xFF];
        // ... 其他 t1/t2/t3 类似计算
        s0 = t0 ^ rk[4*i+4]; // 轮密钥加
        // ... 更新 s1/s2/s3
    }

    // 最终轮(无 MixColumns)
    s0 = SUBWORD(s0) ^ rk[44]; // SUBWORD: SubBytes + ShiftRows
    // ... 类似处理 s1/s2/s3
    PUT_UINT32_LE(s0, out, 0); // 存储密文
}

CUBE_CRYPTO_USE_HW_CRYP 启用时, crypto_aes_crypt_ecb() 的实现变为:

int32_t crypto_aes_crypt_ecb(crypto_aes_context_t *ctx, int32_t mode,
                              const uint8_t *input, uint8_t *output) {
    CRYP_HandleTypeDef hcryp;
    hcryp.Instance = CRYP;
    // 配置 CRYP 外设:算法(AES_ECB)、方向(ENCRYPT/DECRYPT)、密钥大小
    hcryp.Init.DataType = CRYP_DATATYPE_8B;
    hcryp.Init.pKey = ctx->hw_key; // 指向已加载的硬件密钥
    if (HAL_CRYP_Init(&hcryp) != HAL_OK) return CRYPTO_ERR_HW_INIT_FAILED;

    // 启动硬件加密
    if (HAL_CRYP_Encrypt(&hcryp, (uint32_t*)input, 16, (uint32_t*)output, HAL_MAX_DELAY) != HAL_OK) {
        HAL_CRYP_DeInit(&hcryp);
        return CRYPTO_ERR_HW_PROCESS_FAILED;
    }
    HAL_CRYP_DeInit(&hcryp);
    return 0; // 成功
}

工程启示 :硬件加速并非总是最优解。在小数据量(< 64 字节)场景下,软件实现因免去外设初始化、DMA 配置、中断处理等开销,反而更快;而在大数据流(如网络包加解密)中,CRYP 的 DMA 自动搬运能力可释放 CPU 90% 以上负载。 cube_crypto 通过编译期开关,将此决策权交还给系统架构师。

3.2 SHA-256:内存占用与性能的极致压缩

SHA-256 的标准实现需维护 64 字节的哈希状态(8×32-bit)和 64 字节的消息调度缓冲区(16×32-bit),总计 128 字节。 cube_crypto 通过两项关键技术将其压缩至仅 80 字节

  • 状态复用(State Reuse) :在 crypto_sha256_update() 中,不再为每次 update 调用分配独立的 64 字节缓冲区,而是将待处理的输入数据直接映射到哈希状态结构体的尾部预留空间。 crypto_sha256_context_t 定义如下:

    typedef struct {
        uint32_t state[8];     // 32-byte hash state
        uint32_t buffer[16];   // 64-byte message buffer (reused for input)
        uint32_t total[2];     // 64-bit total length counter
        uint8_t  buffer_offset; // 当前 buffer 中已填充字节数 (0-63)
    } crypto_sha256_context_t;
    

    此设计使 context 结构体大小恒为 8*4 + 16*4 + 2*4 + 1 = 97 bytes ,但 buffer 区域在 update 时被动态用作输入暂存,避免额外堆栈分配。

  • 无栈消息调度(Stackless Message Schedule) :标准 SHA-256 需计算 64 个 W[t] 值( t=0..63 ),通常需 256 字节栈空间。 cube_crypto 改用滚动窗口法:仅维护 W[0..15] (初始消息块)和 W[16..63] 的当前计算值 w0,w1,w2,w3 ,通过移位与异或实时生成后续 W[t] ,将栈需求降至 16 字节。

此优化对 RAM 极度紧张的设备(如 STM32L0/L1,SRAM 仅 8-16KB)至关重要,使其能在单个任务栈中同时容纳多个 SHA-256 上下文(如并行校验多个固件分区)。

3.3 ECC 运算:secp256r1 的定点优化

cube_crypto 的 ECC 模块聚焦于 NIST P-256 曲线(secp256r1),因其在安全强度(128-bit)与计算开销间取得最佳平衡,被广泛用于 IoT 设备身份认证。其核心 crypto_ecp 模块采用定点数(Fixed-Point)而非浮点数实现模幂与点乘,原因在于:

  • Cortex-M 系列普遍缺乏硬件浮点单元(FPU),软件浮点模拟开销巨大(>1000 cycles/operation);
  • 定点数可通过 __aeabi_uidiv() 等 CMSIS-DSP 库高效实现模约减。

点乘(Point Multiplication)采用经典的 Montgomery Ladder 算法,其伪代码如下:

Input: Point P, Scalar d (256-bit)
Output: Q = d * P

R0 = Point at Infinity
R1 = P
for i from 255 down to 0:
    if d[i] == 0:
        R1 = R0 + R1   // Differential addition
        R0 = 2 * R0    // Doubling
    else:
        R0 = R0 + R1
        R1 = 2 * R1
return R0

cube_crypto 的实现中, R0 R1 均为 crypto_ecp_point_t 结构体,其字段 X , Y , Z uint32_t[8] 数组(256-bit 定点数,每 uint32_t 存储 32 位)。所有模运算(如 mod p ,其中 p = 2^256 - 2^224 + 2^192 + 2^96 - 1 )均通过专用汇编优化函数 crypto_mpi_mod_p256() 完成,该函数利用 ARM 的 UMULL / MLS 指令实现 64-bit × 64-bit 乘法与 128-bit 减法,将单次模约减控制在 350 cycles 内。

4. 实际工程集成案例

4.1 在 STM32H743 上启用 AES-GCM 硬件加速

假设一个工业传感器节点需对上传的 JSON 数据进行 AES-GCM 加密,以保障云端通信机密性与完整性。以下是完整的集成步骤:

步骤 1:CubeMX 配置

  • 启用 CRYP 外设,时钟源设为 HCLK
  • Project Manager > Advanced Settings 中,将 CRYP Mode 设为 Middleware (而非 Driver ),确保 HAL 库生成 hal_cryp_template.c
  • 生成代码后,在 Core/Inc/cube_crypto_conf.h 中添加:
    #define CUBE_CRYPTO_USE_HW_CRYP 1
    #define CUBE_CRYPTO_USE_HW_HASH 1 // GCM 需要 GHASH,由 HASH 外设加速
    

步骤 2:密钥与 IV 管理

// 使用硬件 RNG 生成强随机 IV
uint8_t iv[12] = {0}; // GCM 标准 IV 长度
RNG_HandleTypeDef hrng;
hrng.Instance = RNG;
HAL_RNG_Init(&hrng);
HAL_RNG_GenerateRandomNumber(&hrng, (uint32_t*)iv);
HAL_RNG_DeInit(&hrng);

// 密钥从安全存储区(如 OTP 或外部 SE)读取
const uint8_t key[16] = { /* 128-bit key */ };

步骤 3:执行 AES-GCM 加密

#include "cube_crypto_gcm.h"

crypto_gcm_context_t gcm_ctx;
uint8_t tag[16];
uint8_t encrypted_data[256];

// 初始化 GCM 上下文
if (crypto_gcm_init(&gcm_ctx, key, 16, iv, 12) != 0) {
    Error_Handler(); // 初始化失败
}

// 添加附加认证数据(AAD),如设备 ID
uint8_t aad[] = "SENSOR_H743_V1";
crypto_gcm_update_aad(&gcm_ctx, aad, sizeof(aad)-1);

// 加密明文
uint8_t plaintext[] = "{\"temp\":25.3,\"hum\":60}";
int ret = crypto_gcm_crypt_and_tag(&gcm_ctx,
                                   CRYPTO_GCM_ENCRYPT,
                                   sizeof(plaintext)-1,
                                   plaintext,
                                   encrypted_data,
                                   tag, 16);
if (ret != 0) {
    Error_Handler(); // 加密或认证失败
}

// 发送 encrypted_data + tag 至云端

关键点 crypto_gcm_init() 内部会检测 CUBE_CRYPTO_USE_HW_CRYP 宏,若启用则调用 HAL_CRYP_Init() 并配置为 CRYP_ALGOMODE_AES_GCM_ENCRYPT crypto_gcm_crypt_and_tag() 则触发 HAL_CRYP_Encrypt() ,由硬件自动完成 AES 加密与 GHASH 计算,CPU 仅需等待 DMA 传输完成。

4.2 在 FreeRTOS 任务中安全调用 ECDSA 签名

一个多任务系统中, Task_Signature 需定期对传感器数据签名,而 Task_Network 可能同时使用 HASH 外设计算 OTA 镜像摘要。为避免硬件资源争用,必须使用互斥锁:

#include "cube_crypto_freertos.h"
#include "cube_crypto_ecdsa.h"

// 全局互斥锁句柄
crypto_mutex_t crypto_mutex;

void vApplicationCreateTasks(void) {
    crypto_mutex = crypto_mutex_create(); // 创建互斥锁
    xTaskCreate(Task_Signature, "SIGN", 256, NULL, 3, NULL);
    xTaskCreate(Task_Network,   "NET",  512, NULL, 3, NULL);
}

void Task_Signature(void *pvParameters) {
    crypto_ecdsa_context_t ecdsa_ctx;
    uint8_t signature[72]; // secp256r1 签名最大 72 字节
    uint8_t data_hash[32];

    while(1) {
        // 1. 计算数据哈希
        crypto_mutex_lock(crypto_mutex);
        crypto_sha256(data, data_len, data_hash);
        crypto_mutex_unlock(crypto_mutex);

        // 2. 执行 ECDSA 签名(内部会再次加锁)
        if (crypto_ecdsa_sign(&ecdsa_ctx,
                              data_hash, 32,
                              private_key, 32,
                              signature, &sig_len) == 0) {
            // 发送 signature
        }
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

cube_crypto_ecdsa_sign() 函数内部会调用 crypto_mutex_lock() ,确保即使 Task_Network 正在调用 crypto_sha256() Task_Signature 也会被阻塞,直至 HASH 外设空闲。这种细粒度的资源保护机制,是 cube_crypto 在复杂 RTOS 环境中稳定运行的基石。

5. 性能基准与资源占用分析

在 STM32H743VI(480MHz Cortex-M7)上, cube_crypto 各模块的典型性能数据如下(基于 IAR EWARM 9.30 编译, -O3 优化):

操作 软件实现 (cycles) 硬件加速 (cycles) 说明
AES-128-ECB (16B) 1,280 320 硬件路径包含 CRYP 初始化(~200 cycles)与核心加密(~120 cycles)
SHA-256 (64B) 4,100 1,850 硬件 HASH 启动开销 ~800 cycles,数据处理 ~1050 cycles
ECDSA Sign (secp256r1) 1,250,000 380,000 PKA 外设加速点乘,减少 70% 时间
RNG 32-bit word 12 纯硬件,无需软件干预

RAM 占用方面, cube_crypto 的静态内存消耗高度可控:

  • 最小配置(仅 AES-ECB 软件版): crypto_aes_context_t (176 bytes) + crypto_sha256_context_t (97 bytes) = 273 bytes
  • 全功能配置(AES-GCM 硬件 + ECDSA + PKA): crypto_gcm_context_t (208 bytes) + crypto_ecdsa_context_t (1,216 bytes) + PKA 工作区 (4KB) = ~5.5 KB

这一特性使其能无缝嵌入 RAM 仅为 64KB 的 STM32L4+ 系列设备,支撑起完整的 TLS 1.2 客户端栈(如移植 Mbed TLS 时,将其 mbedtls_aes_context 等结构体替换为 cube_crypto 对应类型)。

6. 安全实践与常见陷阱规避

cube_crypto 提供了强大的工具,但其安全性最终取决于工程师的使用方式。以下是实践中必须规避的陷阱:

6.1 密钥生命周期管理

  • 陷阱 :将密钥以明文形式硬编码在固件中( const uint8_t key[] = {0x01,0x02,...} )。
  • 正解 :密钥必须存储于受保护区域。对于 STM32,应使用:
    • OTP(One-Time Programmable)存储 :通过 HAL_FLASHEx_OBProgram() 写入 OB.RDP 级别为 LEVEL_1 的 OTP 区,写入后不可读;
    • 外部安全元件(SE) :通过 I2C/SPI 调用 SE 的 GenerateKey Sign 指令,密钥永不离开 SE 芯片;
    • STM32H7 的 TZIC(TrustZone IC) :将密钥置于安全世界(Secure World)内存,非安全世界(NS world)代码无法访问。

6.2 随机数生成的可靠性

  • 陷阱 :在未确认 RNG 外设健康状态下直接调用 HAL_RNG_GenerateRandomNumber()
  • 正解 :必须在每次使用前检查 RNG 状态:
    if (HAL_RNG_GetState(&hrng) != HAL_RNG_STATE_READY) {
        HAL_RNG_DeInit(&hrng);
        HAL_RNG_Init(&hrng);
    }
    // 检查 RNG 是否产生有效随机数(防故障)
    uint32_t rnd1, rnd2;
    HAL_RNG_GenerateRandomNumber(&hrng, &rnd1);
    HAL_RNG_GenerateRandomNumber(&hrng, &rnd2);
    if (rnd1 == rnd2) { // 连续两次相同,判定 RNG 故障
        Critical_Error_Handler(RNG_FAILURE);
    }
    

6.3 侧信道攻击防护

  • 陷阱 :在 crypto_ecdsa_sign() 返回后,未清零栈上的临时变量(如 r , s 值)。
  • 正解 cube_crypto 的所有敏感结构体均提供 zeroize() 函数:
    crypto_ecdsa_context_t ctx;
    // ... 执行签名
    crypto_ecdsa_free(&ctx); // 内部调用 memset_s() 清零 ctx 内存
    

这些实践并非 cube_crypto 库本身的责任,而是嵌入式安全工程师必须内化的工程纪律。库提供了安全的“砖块”,而构建坚不可摧的“城墙”,永远需要工程师亲手垒砌。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐