4. UDS Bootloader刷写流程

任务目标

在UDS Bootloader刷写流程中,安全访问(SecurityAccess,服务ID 0x27)用于防止未授权操作。本案例实现种子生成与密钥验证算法,完成刷写前的安全解锁闭环:ECU端生成种子并校验外部传入的密钥,外部工具端根据同一算法由种子计算密钥。

算法约定

采用轻量级对称算法(非生产级加密,仅演示安全访问机制):

  • 种子为4字节随机数(由ECU生成)
  • 密钥 = 种子与固定密钥字 0x5A5A5A5A 按字节异或后,再与 0xA1 逐字节相加(忽略进位)
  • 安全等级固定为 0x01(刷写所需等级)

完整实现(ECU端 + 外部工具端)

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

/* ========== ECU端:种子生成与密钥验证 ========== */
#define SECURITY_LEVEL 0x01
#define KEY_CONST_1    0x5A5A5A5A
#define KEY_CONST_2    0xA1

/* 模拟ECU内部安全状态 */
static uint8_t current_level = 0x00;

/* 生成4字节种子(实际应用中由硬件随机数产生) */
void SeedGen(uint8_t seed[4]) {
    /* 演示用固定种子,实际应使用真随机数 */
    seed[0] = 0x12; seed[1] = 0x34;
    seed[2] = 0x56; seed[3] = 0x78;
}

/* 根据种子计算密钥(与外部工具端算法一致) */
static void CalcKey(const uint8_t seed[4], uint8_t key[4]) {
    uint32_t seed_val = ((uint32_t)seed[0] << 24) |
                        ((uint32_t)seed[1] << 16) |
                        ((uint32_t)seed[2] << 8)  |
                        (uint32_t)seed[3];
    uint32_t key_val = (seed_val ^ KEY_CONST_1);
    /* 逐字节加常数 */
    uint8_t *kp = (uint8_t *)&key_val;
    for (int i = 0; i < 4; i++) {
        kp[i] = (uint8_t)(kp[i] + KEY_CONST_2);
    }
    key[0] = kp[0]; key[1] = kp[1];
    key[2] = kp[2]; key[3] = kp[3];
}

/* 安全访问服务处理:返回种子或验证密钥 */
bool SecurityAccess(uint8_t sub_func, const uint8_t *data,
                    uint8_t seed_out[4]) {
    if (sub_func == 0x01) {  /* 请求种子 */
        SeedGen(seed_out);
        return true;
    } else if (sub_func == 0x02) {  /* 发送密钥 */
        uint8_t expected[4];
        uint8_t seed[4];
        SeedGen(seed);  /* 实际应缓存上次发送的种子 */
        CalcKey(seed, expected);
        bool match = true;
        for (int i = 0; i < 4; i++) {
            if (data[i] != expected[i]) { match = false; break; }
        }
        if (match) {
            current_level = SECURITY_LEVEL;
            return true;
        }
    }
    return false;
}

/* ========== 外部工具端:由种子计算密钥 ========== */
void ToolCalcKey(const uint8_t seed[4], uint8_t key[4]) {
    CalcKey(seed, key);  /* 复用同一算法 */
}

/* ========== 验证入口 ========== */
#include <stdio.h>
int main(void) {
    uint8_t seed[4], key[4];
    /* 模拟外部工具流程 */
    SecurityAccess(0x01, NULL, seed);   /* 1. 请求种子 */
    ToolCalcKey(seed, key);             /* 2. 计算密钥 */
    bool ok = SecurityAccess(0x02, key, NULL); /* 3. 发送密钥 */
    printf("SecurityAccess %s\n", ok ? "PASS" : "FAIL");
    return ok ? 0 : 1;
}

运行与验证

  • 编译:gcc -o sec_access sec_access.c
  • 运行:./sec_access
  • 通过条件:程序输出 SecurityAccess PASS,退出码为0

该输出由代码逻辑直接推出:种子固定为 0x12345678,异或 0x5A5A5A5A 得 0x486E6C22,逐字节加 0xA1 后密钥为 0xE90F0DC3,验证必然通过。若篡改密钥计算逻辑或种子,程序将输出 FAIL 并返回1。

关键说明

  • 实际ECU中,种子应使用硬件随机数生成器,且每次请求需更新;本案例用固定种子便于验证算法正确性。
  • 生产级实现需引入防重放攻击(种子一次性)、尝试次数限制和更健壮的加密算法(如AES)。
  • UDS服务中,0x27服务的子功能0x01/0x02分别对应请求种子和发送密钥,本代码已按此协议实现。