UDS的安全访问服务(0x27)与CAPL实现自动处理

在实际的诊断中我们要经常和安全访问(Security Access)打交道,为了方便的处理这些问题,写CAPL脚本是一个很好的方案,所以我整理了Vector官方提供的与安全访问(Security Access)有关的函数,并对常用的进行简单说明。

本文CAPL函数有关几乎全部摘录自Vector的官方文档,只是做了整理与翻译。

一、 介绍安全访问(Security Access)

关于ISO 14229的安全访问(Security Access)一个比较好的介绍文档: 安全访问(ISO14229系列之27服务)

当客户端连接到服务端(即ECU)时,客户端有一个会话(session)和一个安全级别(security level)。

1.1 不同的会话(Session)模式

默认情况下,服务器通过为新客户端分配默认会话(Default Session) 来迎接新客户端,在该会话中,只有少数特定服务可以访问,如读取DTC。

但是还有些操作可能涉及到安全或者其他方面的原因,不允许在默认会话(Default Session)中使用,因此如果需要更高的权限,就必须切换到其他的会话中。

在ISO14229中规定了那些服务可以在默认会话中使用,那些不可以在默认会话中使用, 如下:
请添加图片描述

如果需要从默认会话(Default Session)切换到其他会话中使用UDS的10服务, 我们本文介绍的诊断会话(extendedDiagnosticSession) 是ISO 14229规定的必须要提供的会话模式,该会话模式代号通常是03。 也即是说如果要切换到该会话,使用命令为10 03.

1.2 安全访问(Security Access)的工作流程

因为诊断会话(extendedDiagnosticSession) 中包含高权限的操作,因此切换到这个会话后,必须要执行解锁(unlock) 操作才能进行后续的使用。所谓的解锁(unlock) 操作需要使用UDS提供的安全访问(Security Access)服务,也就是UDS的27服务。

0x27 安全访问(Security Access)服务的典型工作流程如下:

  1. Client先主动发送一个: 请求种子(Seed) 的消息
  2. ECU(也就是Server端)收到来自Client的请求后,ECU会发送种子(Seed)
  3. Client在收到ECU发送过来的种子(Seed) 后,客户端对种子(Seed)进行计算,并发送计算后的 密钥(Key)
  4. ECU在收到密钥(Key) 后对其进行校验,如果密钥(Key) 是有效的,那么ECU会将自己解锁,并向Client发送积极响应;否则返回否定响应
1.2.1 安全访问(Security Access)的步骤1:客户端发送请求种子(Seed)

Client端发送请求种子(Seed) 的消息格式如下:
请添加图片描述

虽然第二个参数可以是0x010x030x050x07等奇数,但是我们最常用的还是0x01, 另外securityAccessDataRecord通常为空,所以Client最常用的请求Seed的报文为:27 01

1.2.2 安全访问(Security Access)的步骤2:ECU发送种子(Seed)

ECU端在收到步骤1的请求消息后,如果请求格式正确,那么会发送积极响应,也就是发送种子(Seed) , 消息格式如下:
请添加图片描述

第2个参数一般和Client发送请求时的SubFunction是一致的,也就是最常用的0x01

第3个以及后面的参数securitySeed通常为2、4个字节。

所以ECU最常用的发送Seed的报文为:67 01 XX XX或者67 01 XX XX XX XX

1.2.3 安全访问(Security Access)的步骤3:客户端发送密钥(Key)

Client端发送密钥(Key) 的消息格式如下:
请添加图片描述

虽然第二个参数可以是0x020x040x060x08等偶数(必须为发送Seed时使用的SubFunction加1),但是我们最常用的还是0x02, 也就是最常用的请求报文为:27 02 + securityKey

1.2.4 安全访问(Security Access)的步骤4:ECU发送最终的响应

ECU在收到密钥(Key) 后对其进行校验,于是就分为两种情况:

  • 情况1: 如果密钥(Key) 是有效的,那么ECU会将自己解锁,并向Client发送积极响应。 这种情况下的报文格式比较简单,类似于上面的Table 44。 但是我们最常见的报文还是67 02
  • 情况2: 如果密钥(Key) 是无效的,返回否定响应,格式如下:

请添加图片描述
请添加图片描述

1.3 不同的安全等级(Security level)

安全等级(Security level) 是客户端通过提供安全密钥来解锁服务器内的功能而获得的状态。UDS被设计为允许多达64个安全级别,这些级别是在服务端中设置的布尔标志。

这些安全级别以及它们解锁的内容不是由UDS定义的,而是由ECU制造商定义的。安全级别可以解锁整个服务、子功能或对特定值的访问。例如,写入车辆识别码(VIN)可能需要特定的安全级别,该级别不同于写入最大速度或超越车辆IO所需的安全级别。

下图摘自ISO 14229-1, 描述了SecurityAccess处理的状态图(state chart):
请添加图片描述

上图中的1-10含义,可以参考ISO 14229-1 中的**“Table I.2 — State transitions – disjunctive normal form representation”**

默认会话(Default Session) 中不允许解锁安全等级(Security level) 。要获得一些权限,客户端必须首先切换到启用SecurityAccess服务的非默认会话。只有这样,客户端才能执行握手,从而解锁所需的功能。

在ISO 14229中对于安全等级有两个比较重要的描述:

  1. 在任何时刻,只有一个安全级别处于活动(active)状态
  2. 安全级别(security level)编号是随意的,不同级别之间不意味着有任何关系

二、 介绍常用函数

CAPL中与CRC有关的函数如下:

FunctionsShort Description
diagGenerateKeyFromSeed生成一个密钥(Key)以在ECU内执行安全的诊断功能
diagStartGenerateKeyFromSeed使用 Seed & Key DLL从种子(Seed)开始生成安全密钥(security key)
TestWaitForGenerateKeyFromSeed使用配置的 Seed & Key DLL从种子(Seed)生成安全密钥(security key)
_Diag_GenerateKeyResult显示使用DiagStartGenerateKeyFromSeed启动的安全密钥计算的结果

补充说明:

Windows和Linux支持这些CAPL功能。Linux下的功能尚未经过全面测试。

2.1 函数: diagGenerateKeyFromSeed()

函数功能描述

创建一个密钥(key) 以在设备内执行安全的诊断功能(secured diagnostic functions)
密钥(key) 将与设备的种子(Seed) 一起定义。
如果计算耗时超过1毫秒,则应使用:diagStartGenerateKeyFromSeed_Diag_GenerateKeyResult结合来完成。

函数语法
long diagGenerateKeyFromSeed(byte seedArray[], 
                             dword seedArraySize, 
                             dword securityLevel, 
                             char variant[], 
                             char ipOption[], 
                             byte keyArray[], 
                             dword maxKeyArraySize, 
                             dword& keyActualSizeOut); // form 1

long DiagGenerateKeyFromSeed(char ecuQualifier[], 
                             byte seedArray[] , 
                             dword seedArraySize, 
                             dword securityLevel, 
                             char variant[], 
                             char option[] , 
                             byte keyArray[], 
                             dword maxKeyArraySize, 
                             dword& keyActualSizeOut); // form 2
函数参数介绍
参数含义
seedArray用于生成密钥(key)的种子(Seed)
seedArraySizeSeedArray 参数的字节数
securityLevel创建密钥(Key)使用的安全级别
variant诊断描述(diagnostic description)的变体(Variant)【通俗来讲,是诊断的受体ECU的名称】
ipOption官方文档写的太晦涩了,通俗来讲【诊断的受体ECU的代号】
keyArray使用自定义的DLL创建的密钥(Key)
maxKeyArraySizekeyArray参数允许的最大字节数
keyActualSizeOutkeyArray参数实际使用的字节数
ecuQualifier相应诊断描述的诊断配置对话框中设置的ECU或Target的限定符(Qualifier)

上述参数中variant变量的值可以从CANoe的诊断界面中获取,如下:

请添加图片描述

上述参数中option变量的值可以从函数diagGetCurretnEcu() 的返回中获取,如下:

on diagResponse *
{
  char ecu[100];
  diagGetCurrentEcu(ecu, elcount(ecu));
  write("Received response from %s", ecu);
}

on key 'a'
{
  // Diagnostic description with ECU qualifier 'FunctionalGroup' configured for Functional Group Requests (FGR)
  diagRequest FunctionalGroup.DefaultSession_Start req1;

  diagSendRequest(req1); // Request is sent to functional group, therefore multiple ECUs may respond
}
返回值介绍
返回值含义
0If computation was started
otherwise为了进行进一步的错误分析,您可以使用回调函数_diag_GetError
举例说明

此示例示意性地显示了在CAPL测试模块中使用DiagGenerateKeyFromSeed和回调函数。

Variables
{
  ...
  //actual size of Seed and Key Arrays depend on ECU
  byte gSeedArray[2];                       // 如果是4个字节,数组长度为4
  int gSeedArraySize    = 2;                // 如果是4个字节,这个值改为4
  int gSecurityLevel    = 0x20;             // 这个值往往为0x01
  char gVariant[200]    = "Variant1";       // 基本不影响计算结果
  char gOption[200]     = "option";         // 基本不影响计算结果,一般由OEM定义
  byte gKeyArray[255];                      // 如果是4个字节,数组长度为4
  int  gMaxKeyArraySize = 255;              // 如果是4个字节,这个值改为4
  dword gActualSizeOut     = 0;             // 如果是4个字节,这个值改为4
  char gDebugBuffer[2000];
  diagRequest SecurityAccess::SecuritySeed::Request gSeedReq;
  diagResponse SecurityAccess::SecuritySeed::Request gSeedResp;
  diagRequest SecurityAccess::SecurityKey::Send gKeyReq;
  ...
}

//Unlock ECU by calling customer specific SeedKey DLL (e.g. in a CAPL test module)
{
  ...
  //Request seed from ECU
  diagSendRequest(gSeedReq);
  //Wait until request has been sent completely
  testWaitForDiagRequestSent(gSeedReq, 1000);
  //Wait for response and write seed from response parameter to array
  testWaitForDiagResponse(gSeedReq, 1000);
  diagGetLastResponse (gSeedReq, gSeedResp);
  diagGetParameterRaw (gSeedResp, "Seed", gSeedArray, elcount(gSeedArray));
  //Calculate key
  // _Diag_GetError is called when an error occurs
  if( 0 == diagGenerateKeyFromSeed (gSeedArray, gSeedArraySize, gSecurityLevel, gVariant, gOption, gKeyArray, gMaxKeyArraySize, gActualSizeOut))
  {
    //Write result to diagnostic request
    diagSetParameterRaw(gKeyReq, "Key", gKeyArray, gActualSizeOut);
    //Send Key to unlock ECU
    testWaitForDiagRequestSent(gKeyReq, 1000);
  }

//Callback function for error handling (optional)
_diag_GetError (char buffer[])
{
  //called if error in diagGenerateKeyFromSeed occurs
  snprintf(gDebugBuffer,elcount(gDebugBuffer),"%s", buffer);
  write("CALLBACK %s", gDebugBuffer);
}

2.3 函数: diagStartGenerateKeyFromSeed()

函数功能描述

使用种子(Seed)和密钥(Key)的DLL文件,从种子(Seed) 开始生成安全密钥(security key)。如果无法启动计算,则返回一个错误。

通过调用函数_Diag_GenerateKeyResult来显示计算结果。请注意,生成安全密钥(security key)的计算时间可能会超过1ms,这将导致CANoe中的实时事件(real-time event)处理出现问题。因此,计算是在后台进行的,即不能立即获得结果。

在测试节点中,可以使用函数TestWaitForGenerateKeyFromSeed

如果保证计算时间远小于1毫秒,则可以使用diagGenerateKeyFromSeed

函数语法
long diagStartGenerateKeyFromSeed(byte seedArray[], 
                                  dword seedArraySize, 
                                  dword securityLevel); // form 1

long diagStartGenerateKeyFromSeed(byte seedArray[], 
                                  dword seedArraySize, 
                                  dword securityLevel , 
                                  char variant[], 
                                  char option[]); // form 2

long diagStartGenerateKeyFromSeed(char ecuQualifier[], 
                                  byte seedArray[], 
                                  dword seedArraySize, 
                                  dword securityLevel); // form 3

long diagStartGenerateKeyFromSeed(char ecuQualifier[], 
                                  byte seedArray[], 
                                  dword seedArraySize, 
                                  dword securityLevel, 
                                  char variant[], 
                                  char option[]); // from 4
函数参数介绍
参数含义
seedArrayECU返回的种子(Seed)字节
seedArraySizeSeedArray 参数的字节数
securityLevel创建密钥(Key)使用的安全级别
variant诊断描述(diagnostic description)中定义的Variant 限定符。在form 1中,该值将是为诊断描述配置的变量
option未来该选项可以转发到DLL。如果不存在或传入空字符串,则该值可能从通信状态产生,例如ECU所处的诊断会话。
ecuQualifier相应诊断描述的诊断配置对话框中设置的ECU或Target的限定符(Qualifier)
返回值介绍
返回值含义
0If computation was started
otherwise详见 CAPL Functions–> Diagnostics --> Error Codes
举例说明

按下u键后解锁ECU:

_Diag_GenerateKeyResult( long result, BYTE computedKey[])
{
  diagRequest SendKeyLevel1 rqSendKey;

  if( 0 != result)
  {
    write( "Error: computing key returned %d", result);
    return;
  }

  // Success, i.e. a key was computed, so send it to the ECU

  rqSendKey.SetParameterRaw("Key", computedKey, elcount( computedKey));
  rqSendKey.SendRequest();
}

On DiagResponse RequestSeedLevel1
{
  BYTE seed[4];
  count = this.GetParameterRaw("Seed", seed, elcount(seed));
  // _Diag_GetError is called when an error occurs
  DiagStartGenerateKeyFromSeed(seed, elcount(seed), 1);
}

_Diag_GetError (char buffer[])
{
  //called if error in DiagGenerateKeyFromSeed occurs
  write("Diagnostic Error: %s", buffer);
}

On key 'u' // unlock
{
  diagRequest RequestSeedLevel1 rqRequestSeed;
  rqRequestSeed.SendRequest();
}

2.4 函数: TestWaitForGenerateKeyFromSeed()

函数功能描述

使用配置的Seed&Key DLL 从种子(seed)生成安全密钥(security key)。对DLL的调用可能需要更多的时间,因此测试模块可能希望等待结果,以免干扰测试的实时执行(real-time execution)。

函数语法
long TestWaitForGenerateKeyFromSeed(byte seedArray[], 
                                    dword seedArraySize, 
                                    dword securityLevel, 
                                    byte keyArray[], 
                                    dword maxKeyArraySize, 
                                    dword& keyArraySizeOut, 
                                    dword appTimeout_ms); // form 1

long TestWaitForGenerateKeyFromSeed(byte seedArray[], 
                                    dword seedArraySize, 
                                    dword securityLevel, 
                                    char variant[], 
                                    char option[], 
                                    byte keyArray[], 
                                    dword maxKeyArraySize, 
                                    dword& keyArraySizeOut, 
                                    dword appTimeout_ms); // form 2

long TestWaitForGenerateKeyFromSeed(char ecuQualifier[], 
                                    byte seedArray[], 
                                    dword seedArraySize, 
                                    dword securityLevel, 
                                    byte keyArray[], 
                                    dword maxKeyArraySize, 
                                    dword& keyArraySizeOut, 
                                    dword appTimeout_ms); // form 3

long TestWaitForGenerateKeyFromSeed(char ecuQualifier[], 
                                    byte seedArray[], 
                                    dword seedArraySize, 
                                    dword securityLevel, 
                                    char variant[], 
                                    char option[], 
                                    byte keyArray[], 
                                    dword maxKeyArraySize, 
                                    dword& keyArraySizeOut, 
                                    dword appTimeout_ms); // form 4
函数参数介绍
参数含义
seedArrayECU返回的种子(Seed)
seedArraySizeSeedArray 参数中的字节数
securityLevel应解锁哪个安全级别(security level)?
variant指示当前ECU variant的可选参数。在form 1中,该值不必由CAPL程序提供,而是取自诊断描述配置。
option可选参数,提供影响密钥计算的附加信息,例如当前激活的诊断会话。在form 1中,该值不必由CAPL程序提供,但如果配置和设置,则从当前ECU状态中获取
keyArray计算的结果被写入这个数组
maxKeyArraySizeDLL返回的keyArray参数中允许的最大字节数
keyArraySizeOutDLL返回的keyArray参数实际使用的字节数
appTimeout_ms函数等待DLL中的计算函数返回的最长时间(以毫秒为单位); 如果DLL需要更长的时间,则会报告超时。
ecuQualifier相应诊断描述的诊断配置对话框中设置的ECU或Target的限定符(Qualifier)
返回值介绍
返回值含义
1密钥(Key)生成成功
0超时–DLL没有及时返回密钥
举例说明

TestWaitForGenerateKeyFromSeed 函数示例代码:

Testfunction ComputeKeyInExtendedSession( BYTE seed[], BYTE key[])
{
  DWORD keyLenOut;
  keyLenOut = 0;
  DiagSetCurrentSession(0x03); // extended session
  // The key computation may use the current session as optional argument
  TestWaitForGenerateKeyFromSeed( seed, elcount( seed), 1, key, elcount(key), keyLenOut, 1000);
}

2.5 函数: _Diag_GenerateKeyResult()

函数功能描述

显示使用DiagStartGenerateKeyFromSeed函数启动的安全密钥(security key)计算的结果。

函数语法
void _Diag_GenerateKeyResult(long result, 
                             BYTE computedKey[]);
函数参数介绍
参数含义
result0: Success other: Error code
computedKeySeed & Key DLL 返回的密钥Key, 如果有的话
举例说明

示例代码见 DiagStartGenerateKeyFromSeed 函数的介绍

  • 6
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
CAPL 是矢量公司开发的一种脚本语言,主要用于 CAN 和 LIN 网络的测试和仿真。在实现 UDS 刷写时,可以通过 CAPL 脚本与 UDS 协议栈进行交互,实现数据的收发和处理。以下是一个简单的 CAPL 脚本示例,用于发送 UDS 诊断请求并接收响应: ```c variables { message UDS_Request; // UDS 诊断请求消息 message UDS_Response; // UDS 诊断响应消息 dword address = 0x12345678; // 要刷写的地址 dword data = 0x11223344; // 要写入的数据 } on start { // 初始化 CAN 通道 setCanChannel(1); setBaudrate(500000); setCanBusOutputControl(1); // 初始化 UDS 协议栈 UDS_Init(); } on timer event { // 构造 UDS 诊断请求消息 UDS_Request.ID = 0x7E8; UDS_Request.Data(0) = 0x22; // 诊断服务 ID UDS_Request.Data(1) = address >> 24; // 地址高位 UDS_Request.Data(2) = address >> 16; // 地址次高位 UDS_Request.Data(3) = address >> 8; // 地址次低位 UDS_Request.Data(4) = address; // 地址低位 UDS_Request.Data(5) = data >> 24; // 数据高位 UDS_Request.Data(6) = data >> 16; // 数据次高位 UDS_Request.Data(7) = data >> 8; // 数据次低位 UDS_Request.Data(8) = data; // 数据低位 UDS_Request.len = 9; // 发送 UDS 诊断请求消息 output(Uds_Request); // 等待 UDS 诊断响应消息 while(!Uds_Response.valid) delay(10); // 处理 UDS 诊断响应消息 if(Uds_Response.Data(0) == 0x62) // 诊断响应 ID { if(Uds_Response.Data(1) == 0x22) // 诊断服务 ID { if(Uds_Response.Data(2) == 0x00) // 响应报文子函数 ID { // UDS 诊断请求成功,可以进行下一步操作 // ... } else { // UDS 诊断请求失败,处理错误码 // ... } } else { // UDS 诊断请求失败,处理错误码 // ... } } } ``` 该示例中,通过定时器事件周期性发送 UDS 诊断请求消息,并等待 UDS 诊断响应消息。在接收到响应消息后,根据响应数据判断 UDS 诊断请求是否成功,如果成功则可以进行下一步操作,否则需要处理错误码。需要注意的是,该示例仅用于演示 UDS 刷写的基本流程,实际应用中需要根据具体需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值