文章目录
前言
背景:由于UDS服务$27安全服务中需要ECU生成随机数作为种子,且ECU使用的MCU(RH850)没有随机数生成器,因此需要通过算法实现随机数。
真正意义的随机数生成器存在吗? - 知乎https://www.zhihu.com/question/20145173要求:生成的随机数种子1000次不能重复,种子长度为3 Bytes。
实现:通过随机算法实现种子生成,并通过CAPL程序检查种子重复率问题。
注:本文重在CAPL测试程序,而非随机数算法!
一、随机数生成算法
由于C标准库中的rand函数每次得到的序列都是相同的,如果想得到不同序列需要调用srand设置不同的种子,做法如下:
- 获取随机数之前,使用单片机RTC/当前电压AD值/或全局GlobalTime(从整车CAN网络接收的时间)等作为srand 的种子以初始化随机数序列;
- 确认rand随机数范围(所使用stdlib.h库中RAND_MAX为0x7FFF,无符号数);
示例代码(以RTC作为srand种子):
FUNC(Std_ReturnType, DCM_APPL_CODE) SecurityAccess_Level1_GetSeed(
Dcm_OpStatusType OpStatus,
P2VAR(uint8, AUTOMATIC, DCM_VAR) Seed,
P2VAR(Dcm_NegativeResponseCodeType, AUTOMATIC, DCM_VAR) ErrorCode
)
{
static boolean isFirstRequest = TRUE;
uint32 random = 0U;
(void)OpStatus;
/* Initialize random number generator with seed(RTC).*/
if (isFirstRequest) {
const uint32 currSysTime = SystemTime_Read_ticks_32bits();
srand(currSysTime);
isFirstRequest = FALSE;
} else {
;
}
/* Generate the random number. the RAND_MAX is 0x7FFF in _gh stdlib.*/
random = (uint32)(rand());
random += (uint32)(rand()<< 15U);
Seed[0] = (uint8)(random);
Seed[1] = (uint8)(random >> 8U);
Seed[2] = (uint8)(random >> 16U);
*ErrorCode = DCM_E_POSITIVERESPONSE;
return E_OK;
}
二、CAPL程序测试
1.关联类型&用法
为了实现快速查找比对重复的随机数,需要使用到类似C++中的map容器进行查找,而map集合特点:
- 采用了 Key-value键值对映射的方式进行存储,且存储是无序的(散列的);
- Key在Map里面是唯一的但是value可以重复,一个Key对应一个Value;
而CAPL有一种特殊的关联类型(Associative Fields),类似Python字典和C++ Map容器,其关联类型的元素也是键值对(key value pairs)。
从CANoe帮助文档中查看用法,如下:
关联类型定义格式如下,对象左边是Value类型,右边[ ]内是Key类型:
int m[ float ]; // maps floats to ints
float x[ int64 ]; // maps int64s to floats
char[30] s[ char[] ]; // maps strings (of unspecified length) to strings of length < 30
官方示例1:关联浮点型
float m[float]; // 定义关联对象
m[4.1] = 5.5; // key is 4.1 (float) and value is 5.5 (float)
m[5.3] = 6.6;
write ("4.1 is mapped to %2.2lf", m[4.1]);
write ("5.3 is mapped to %2.2lf", m[5.3]);
for (float mykey : m)
{
write("%2.2lf is mapped to %2.2lf.", mykey, m[mykey]);
}
官方示例2: 关联字符串
char[30] namen[char []]; // 定义关联对象
strncpy(namen["Max"], "Mustermann", 30); // 相当于 namen["Max"] = "Mustermann"
strncpy(namen["Vector"], "Informatik", 30); // 相当于 namen["Vector"] = "Informatik"
for (char[] mykey : namen)
{
write("%s is mapped to %s", mykey, namen[mykey]);
}
关联对象方法(Associative Fields CAPL Functions):
本测试代码则用到map中的key唯一的特性,将随机数作为key,对应的value可以是任意类型数据(由于不关心,统一设置为整型数据1),然后通过::containsKey()方法进行查找。
如果::containsKey(randomNum)返回1,则表示该key(随机数randomNum)已经存在,否则将其插入到关联类型中。
2.测试程序
假设:
- 诊断物理请求ID & 响应ID分别为:0x705 & 0x745
- 通过安全访问服务$27 01 请求获得种子(随机数,3个字节)
代码如下(示例):
on message 0x745 /* VCU_DiagResponse.*/
{
int seedMap[long]; /* Maps long to int */
byte isDuplicateOccur = 0;
long randomNum = 0;
long index = 0;
if ( (!isDuplicateOccur)
&& (this.byte(1) == 0x67) && (this.byte(2) == 0x01))
{
randomNum = this.byte(3);
randomNum <<= 8;
randomNum += this.byte(4);
randomNum <<= 8;
randomNum += this.byte(5);
/* Check if this random number is duplicated,
* if not - insert this random number as key and the value is 1.*/
if (seedMap.containskey(randomNum)) {
write("Seed %6d: %06X =======> Duplicated seed is FOUND!!!", index++, randomNum);
isDuplicateOccur = 1;
} else {
seedMap[randomNum] = 1;
write("Seed %6d: %06X", index++, randomNum);
}
#if (0) /* CAPL map test code.*/
switch (index)
{
case 1:
seedMap[0x123456] = 1;
write("Seed %6d: %06X", index++, 0x123456);
break;
case 1000:
if (seedMap.containskey(0x123456)) {
write("Seed %6d: %06X =======> Duplicated seed is FOUND!!!", index++, 0x123456);
isDuplicateOccur = 1;
}
break;
default:
break;
}
#endif
}
}