上一节我已经讲解了UDS诊断的3个常用服务,10,14,28服务,本节接着讲解最核心的安全解锁算法27服务。

上表就是所有的UDS诊断服务,应该有25条左右。
下面就来讲讲27安全访问服务
1。安全访问 SecurityAccess(27h)
27服务提供了一种访问那些权限受限或与排放及安全因素有关的数据及服务的方法。
例如,上传/下载程序或数据至 ECU、从 ECU 中读取特殊位置内存数据等诊断服务一般需要执行安全访问。因为下载不恰当的程序或数据至 ECU 可能破坏电子设备或其它汽车部件,或对汽车的排放、安全性及安全标准造成风险。
安全访问的概念使用“种子”和“密钥”来实现。第一步,诊断工具发送“SecurityAccess-RequestSeed”服务报文。ECU 发送一个种子进行响应,此种子是诊断工具及 ECU 计算密钥的输入参数。
第二步,诊断工具通过发送包含密钥的“SecurityAccess-SendKey”服务报文给 ECU来请求比较密钥。ECU 须将此密钥与内部存储或计算的密钥进行比较,如果两数相符,ECU 使 能 ( 解 锁 ) 诊 断 工 具 对 特 定 服 务 和 数 据 的 访 问 权 限 , 并 通 过“SecurityAccess-SendKey”肯定响应报文指出。如果两数不相符,此访问被认为是一次错误的访问尝试。如果访问因其它原因被拒回,此访问并不被认为是一次错误的访问尝试。无效的密钥要求诊断工具从头开始重新发送“SecurityAccess-RequestSeed”请求报文。安全访问的流程参见下图。

27报文格式
请求种子

肯定响应

否定响应

发送密钥
请求


肯定响应

否定响应

安全访问等级

如果诊断工具发送一个无效的密钥,ECU 拒绝请求并发送否定响应码 35h“密钥无效”(InvalidKey),安全访问错误计数加 1。该计数器的初始值为零。
当错误计数器数值达到 3 时,ECU 需要等待 10 秒方可接受下次“请求种子”(RequestSeed)报文并返回 36h(请求次数超出限制)否定响应。在这 10s 内,任何“SecurityAccess-RequestSeed”都将不被处理,且 ECU 返回 37h(延时时间未到)否定响应。当 10 秒等待时间结束,安全访问错误计数减 1 并允许另一次尝试。如果在这次尝试期间安全访问错误计数再次增加(由于密钥无效),要求 ECU 在接受下次“请求种子”(Request Seed)报文前再次等待 10 秒。
ECU 上电或复位后默认处于闭锁状态,且需启动 10s 的安全访问延时时间,同时错误计数器重置为零。

详细代码如下:

#ifndef _SID27_SECURITYACCESSCFG_H
#define _SID27_SECURITYACCESSCFG_H
#include "SID27_SecurityAccess.h"
#include "aes_cbc_cmac.h"
/******************************************************************************
** Macro Definitions
******************************************************************************/
#define SID_SA_SUBFUNC_NUMBER 6U
/******************************************************************************
** Export Functions
******************************************************************************/
extern const Struct_Uds_SID_SA Uds_SID_SA[SID_SA_SUBFUNC_NUMBER];
#endif
#include "SID27_SecurityAccessCfg.h"
#include "Dcm_DrvCfg.h"
/******************************************************************************
******************************************************************************/
boolean SID_SA_SUBID_RSD_Callback(uint8_t *pInd, uint8_t *pRes, uint32_t *pResLen);
boolean SID_SA_SUBID_SK_Callback(uint8_t *pInd, uint8_t *pRes, uint32_t *pResLen);
/*** ***************************************************************************
******************************************************************************/
/******************************************************************************
******************************************************************************/
const Struct_Uds_SID_SA Uds_SID_SA[SID_SA_SUBFUNC_NUMBER] =
{
{0x01U,SID_SA_SUBID_RSD_Callback},
{0x02U,SID_SA_SUBID_SK_Callback},
{0x03U,SID_SA_SUBID_RSD_Callback},
{0x04U,SID_SA_SUBID_SK_Callback},
{0x05U,SID_SA_SUBID_RSD_Callback},
{0x06U,SID_SA_SUBID_SK_Callback},
};
#define SEED_LEN (16)
uint8_t Uds_Seed[SEED_LEN]; / Seed Buf /
/******************************************************************************
******************************************************************************/
/*************************************************************************/
/*
Function: boolean SID_SA_SUBID_RSD_Callback
(uint8_t *pInd, uint8_t *pRes, uint32_t *pResLen)
Description:
Parameters: pInd
pRes
pResLen
Return: boolean
Return Values: E_OK
E_NOT_OK
*/
boolean SID_SA_SUBID_RSD_Callback(uint8_t *pInd, uint8_t *pRes, uint32_t *pResLen)
{
boolean ret;
uint8_t i;
if (Uds_SID_SA_Info.AccessDelay == 0U)
{
if (Uds_SID_SA_Info.Security_Locked == false)
{
for(i=0;i<SEED_LEN;i++)
{
Uds_Seed[i] = 0x00U;
}
}
else
{
GetRandom(SEED_LEN, Uds_Seed);
}
if (Uds_SID_SA_Info.RequestSeedFlag != true)
{
Uds_SID_SA_Info.RequestSeedFlag = true;
}
else
{
}
pResLen[0U] = SEED_LEN;
for(i=0;i<SEED_LEN;i++)
{
pRes[i] = Uds_Seed[i];
}
Uds_Response.ResCode = UDS_RES_CODE_PR;
}
else
{
Uds_Response.ResCode = UDS_RES_CODE_RTDNE;
}
ret = E_OK;
return ret;
}
/*************************************************************************/
/*
Function: boolean SID_SA_SUBID_SK_Callback
(uint8_t *pInd, uint8_t *pRes, uint32_t *pResLen)
Description:
Parameters: pInd
pRes
pResLen
Return: boolean
Return Values: E_OK
E_NOT_OK
*/
boolean SID_SA_SUBID_SK_Callback(uint8_t *pInd, uint8_t *pRes, uint32_t *pResLen)
{
boolean ret;
uint8_t loop;
uint32_t seed;
uint8_t key[SEED_LEN];
if (Uds_SID_SA_Info.RequestSeedFlag == true)
{
EncryptData(Uds_Seed, SEED_LEN, key);
for (loop = 0U; loop < SEED_LEN; loop++)
{
if (pInd[loop] != key[loop])
{
break;
}
}
Uds_SID_SA_Info.RequestSeedFlag = false;
Uds_SID_SA_Info.FailCount++;
if (Uds_SID_SA_Info.FailCount > 2U)
{
Uds_SID_SA_Info.AccessDelay = 10000 * UDS_MS_MULTI_FACTOR;
Uds_Response.ResCode = UDS_RES_CODE_ENOA;
}
else
{
Uds_Response.ResCode = UDS_RES_CODE_IK;
}
}
else
{
Uds_Response.ResCode = UDS_RES_CODE_RSE;
}
ret = E_OK;
return ret;
}
#include "hal_data.h"
#include "bsp_debug_uart.h"
#include "bsp_canfd1.h"
#include "bsp_canfd0.h"
#include "Systick.h"
#include "CANFD_A2B.h"
#include "AB_Swap_Config.h"
#include "AB_Swap.h"
#include "UDS.h"
#include "uds_user.h"
extern volatile bool uart_send_complete_flag;
/* 外部变量和函数声明 */
extern volatile bool canfd0_rx_complete_flag;
extern can_frame_t canfd0_tx_frame;
extern can_frame_t canfd0_rx_frame;
//CAN
extern volatile bool canfd1_rx_complete_flag;
extern can_frame_t canfd1_rx_frame;
extern can_frame_t canfd1_tx_frame;
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
static const uint8_t txData[10] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x70, 0x80,0xff,0xaa};
static uint8_t DataBuf0[64] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64};
static uint8_t DataBuf1[64] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64};
/*******************************************************************************************************************//**
-
main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
-
is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
Debug_UART9_Init();
hal_systick_init();
CANFD1_Init();
UDSInit();
printf("RA8D1 UDS诊断测试开始\r\n");
while(1)
{
//HAL_Delay(500);
//CAN1_SendMessage(0x666, DataBuf0, 8);
//CANFD1_SendMessage(0x777, DataBuf0,64);
if(g_1ms_Falg == 1)
{
g_1ms_Falg = 0;
UDSmain();
}
#if 1
if (true == canfd1_rx_complete_flag)
{
canfd1_rx_complete_flag = false;
TpFunc_Recv_Handle(canfd1_rx_frame.id, canfd1_rx_frame.data, canfd1_rx_frame.data_length_code);
printf("Canfd6 Last Receive Pdu: \\r\\n");
printf("ID: 0x%08x \\r\\n", canfd1_rx_frame.id);
printf("DataLength: %d \\r\\n", canfd1_rx_frame.data_length_code);
printf("Data: \\r\\n");
for (uint32_t i = 1; i <= canfd1_rx_frame.data_length_code; i++)
{
printf("%d ", canfd1_rx_frame.data[i - 1]);
if (i % 8 == 0)
{
printf("\\r\\n");
}
else
{
}
}
printf("\\r\\n");
}
#endif
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
/*******************************************************************************************************************//**
-
This function is called at various points during the startup process. This implementation uses the event that is
-
called right before main() to set up the pins.
-
@param[in] event Where at in the start up process the code is currently at
**********************************************************************************************************************/
void R_BSP_WarmStart (bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0
R_FACI_LP->DFLCTL = 1U;
#endif
}
if (BSP_WARM_START_POST_C == event)
{
R_IOPORT_Open(&g_ioport_ctrl, &IOPORT_CFG_NAME);
}
}
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADER
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();
/* Trustzone Secure Projects require at least one nonsecure callable function in order to build (Remove this if it is not required to build). */
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ()
{
}
FSP_CPP_FOOTER
#endif
烧录代码到板子


打开CANtools上位机
先发种子请求 27 01

在发送秘钥
27 02 0D 4D E0 52 27 2C C4 F5 6E 2A 4F BC 8D CF A9 31

可以发现,回复67 02
表示成功解锁
我们发个错的秘钥,看是什么情况
27 02 27 02 0D 4D E0 52 27 2C C4 F5 6E 2A 4F BC 8D CF A9 30
将最后的31改为30

发现回复7F 27 13
回复错误代码13,表示服务不支持
详情见视频,至此27服务讲解完毕!!!!!