1、E2E的Profile(配置)
1.1 概述
E2E提供了一下五种保护机制来保护数据安全,如下:
CRC:发送端根据数据计算CRC值,接收端会重新计算并检查。
Sequence Counter: 发送端每次传输,该值都会加一,接收端会检查该值。
Alive Counter:发送端每次传输,该值都会加一,接收端会检查该值,通常跟Sequence Counter是一个东西(具体我也不知道有什么区别)。
Data ID:给每个数据或者I-PDU Group编号。
Timeout detection: 接收者接收超时,发送者响应超时。
为了同时满足保护机制的标准化和灵活性要求,E2E 模块提供了很多个 profile,包括P01、P02、P04、P05 和 P06,P07, P11, P22,它们之间的最大区别就在于每一个所采用的保护机制是以上五个保护机制的子集。
Profile | 保护机制 | 发送描述 | 最大保护数据长度 |
Profile01 | Counter(4bit)、Data ID(16bit)、 CRC8、Timeout monitoring | Data ID由Data ID Mode决定是否发送 ,Counter和CRC需要发送 | 30bytes |
Profile02 | Counter(4bit)、Data ID List(8bit)、 CRC8 | Data ID 不会发送,仅用作 CRC计算,Counter和CRC需要发送 | 256bytes |
Profile04 | Counter(16bit)、Data ID(32bit)、CRC32、Length(16bit) | Counter、Data ID、CRC、Length均会发送 | 4096bytes |
Profile05 | Counter(8bit)、Data ID(16bit) 、CRC16 | Data ID 不会发送,仅用作 CRC计算,Counter和CRC需要发送 | 4096bytes |
Profile06 | Counter(8bit)、Data ID(16bit)、CRC16、Length(16bit) | Data ID 不会发送,仅用作 CRC 计算,Counter,CRC和Length需要发送 | 4096bytes |
Profile07 | Counter(32bit)、Data ID(32bit)、CRC64、Length(32bit) | Counter、Data ID、CRC、Length均会发送 | 未知 |
Profile11 | Counter(4bit)、Data ID(16bit or 12bit)、CRC8 | Counter、Data ID、CRC均会发送 | 30bytes |
Profile12 | Counter(4bit)、Data ID List(16*8bit)、CRC8 | Data ID 不会发送,仅用作 CRC计算,Counter和CRC需要发送 | 未知 |
-
对于CAN/CANFD/LIN通信信道,需保护的完整数据(包括应用数据、CRC和Counter)的长度不超过32字节,应通过E2E profile1进行保护
-
对于CANFD通信信道,需保护的完整数据(包括应用数据、CRC和Counter)的长度超过32bytes,应通过E2E profile5进行保护
-
对于ETH通信信道,需保护的完整数据(包括应用数据、CRC和Counter)的长度不超过4k bytes,应通过E2E profile4进行保护
-
对于ETH通信信道,需保护的完整数据(包括应用数据、CRC和Counter)的长度超过4k bytes但不超过4M bytes,应通过E2E profile7进行保护
1.2 Profile1
1.2.1 例子
下面的例子可以简单介绍E2E的工作机制,假设汽车内有两个ECU,A和B,两个节点之间通过CAN总线进行通信,A节点要将某一安全相关的数据(比如汽车车速信号,油门踏板信号等)传给B节点,这里我们假定车速信号VehSpeed = 0x1157,如果采用E2E概念对这个车速数据进行保护,那么实际上A除了要发给B上边VehicleSpeed数据之外,还要将CRC和Counter传给B,B在接受到数据之后也会计算出CRC然后将其和收到的进行比较,B会根据校验的结果进行下一步操作。
-
那么现在的问题就是已知数据Data=0x1157,计算出CRC、Counter。
Profile1的一些属性:CRC,Counter,Timeout monitoring,Data ID
CRC:对数据进行多项式除法计算后的余数
Counter:用于计数每次增加1,A将计数值发给B,B可以依据收到的counter值确定是否接收及时。
Timeout monitoring:用来评价Counter是否丢失延时等。
DataID:一个A和B提前定好的特殊数字,一般是16bits,按照E2E_P01DataIDMode 的不同分为1A,1B,1C三种细分的配置,计算CRC时对DataID做不同处理。
在考虑上述Autosar要求后,我们要计算CRC的数据最终可写为Data = {0x09,0x00,0x00,0x57,0x11};这里我们假定DataID=0x0009,VehSpeed低8位放到数组靠前位置。
总结一下就是下面的过程
1.将counter放入Data
2.将DataID(16bits)按照一定规则放入Data
3.计算Data的CRC值
4.将counter增加1
1.2.2 profile 1 CRC的计算
profile 01的CRC计算基于 user data
关于CRC计算,CRC可以理解为多项式做除法后的余数,按照除数的不同可以分类为CRC-8,CRC-16,CRC-32等,看了一下大概好几十种。Autosar E2E profile 1 规定采用CRC-8-SAE J1850 ,对应多项式 x8+x4+x3+x2+1 ,即100011101,通常写为0x1D,注意这里不是0x11D,可能是最高位必然为1,所以省去了。
下面图片显示了二进制除法采用多项式计算0x57: CRC=0x8D的过程,实际上0x57按照不同的传输顺序分为两种Msbit-first(01010111)Lsbit-first(11101010)这里只计算Msbit-first的情况。后边添加了8个padded bits,但是观察下来猜测若要计算CRC-3要padded 3 bits?CRC-16要padded 16 bits??
取一个字符(8bit),逐位检查该字符,如果为1,字符^crc_mul;同时,如果原本crc最高位是1,那么crc^crc_mul后左移1位,否则只是左移一位
不取反 从高位开始异或 左移CRC
1.2.3 profile 1 工作机制图示
2、CRC-16代码实现
2.1 直接计算法
#include <iostream>
#include <cstdint>
// 计算CRC16校验值
uint16_t calculateCRC16(const uint8_t* data, size_t length)
{
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < length; ++i)
{
crc ^= data[i];
for (int j = 0; j < 8; ++j)
{
if (crc & 0x0001)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
int main()
{
// 示例数据
uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
size_t length = sizeof(data) / sizeof(data[0]);
// 计算CRC16校验值
uint16_t crc16 = calculateCRC16(data, length);
// 输出结果
std::cout << "CRC16校验值: 0x" << std::hex << crc16 << std::endl;
return 0;
}
2.2 查表法
2.2.1 表的生成算法
#define POLY 0xA001 // 生成多项式Ox8005的反序,即1000000000000101→1010000000000001,即A001
unsigned short auchCRC[256]; // CRC表
void make_crc_table(void) // 生成CRC表的函数
{
unsigned short c;
int i, j;
for (i = 0; i < 256; i++)
{
c = i;
for (j = 0; j < 8; j++)
{
if (c & 0x1) // 如果最低位为1,则右移一位并与生成多项式异或(商1)
c = (c >> 1) ^ POLY;
else // 如果最低位为0,则只右移一位,无需异或(商0)
c = c >> 1;
}
auchCRC[i] = c; // 将计算出的CRC校验码存入表中
}
}
2.2.2 查表法
const UINT16 auchCRC[256] =
{
0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040
};
//计算CRC16码,使用查表法
UINT16 CRC16(u8 *data, int len)
{
UINT16 CRC = 0xffff;//0xff与数据异或就相当于取反,故只需要一开始把CRC初始值设置为0xffff即可
UINT8 index;
for (int i = 0; i < Len ; i++)
{
index = (CRC & 0xFF)^ data[i];//取上一字节的CRC低八位,与本字节异或
CRC >>= 8;//取上一字节的高八位
CRC ^= auchCRC[index];//与查表后的结果异或
}
return (CRC);
-
初始值是给CRC计算一个初始值,可以是0,也可以是其他值;结果异或值是把计算结果再异或某一个值;这么做的目的是防止全0数据的CRC一直为0。
-
输入数据反转是指输入数据以字节为单位按位逆序处理;输出数据反转是指CRC计算结果整体按位逆序处理;这么做的目的我看到的一个合理解释是右移比左移更容易计算,效率高,它跟大小端无关。
3、P01 的e2e 配置
//========================server==================================================
"e2e" :
{
"e2e_enabled" : "true",
"protected" :
[
{
"service_id" : "0x1111",
"event_id" : "0x3333",
"profile" : "CRC8",
"variant" : "protector",
"crc_offset" : "0",//CRC存放的偏移位置,一般放在payload的第一个字节
"data_id_mode" : "3",
"data_length" : "56",//(payload 的长度-1)*8,单位bit
"data_id" : "0xA73"
}
]
},
//=========================client===================================================
"e2e" :
{
"e2e_enabled" : "true",
"protected" :
[
{
"service_id" : "0x1111",
"event_id" : "0x3333",
"profile" : "CRC8",
"variant" : "checker",
"crc_offset" : "0",
"data_id_mode" : "3",
"data_length" : "56",
"data_id" : "0xA73"
}
]
},