本文仅介绍基本CAN报文收发的实现,所有USB-CAN硬件为ValueCAN;
pip install pycryptodome
pip install python-can
pip install python-ics
for value_can driver with python-can, the script must run under 32bit python!!!
1 组帧
#!/usr/bin/env python3
# File Name:UDSTransLayer.py
# Author :Patek
# Data :2019-03-04
# Note : This script is used to $27 service of UDS.
import can
#import math
#import SecurityAccessAlgrithm as SA
#函数名:fun_Bytes_To_Hex_InStr
#参 数:bytes类型
#返 回:str类型
#说 明:将列表十进制数据元素转换为十六进制并以字符串形式返回,主要用于数据十六进制打印便于查看
def fun_Bytes_To_Hex_InStr(bytes):
l = [('%02x' %i) for i in bytes]
return "|".join(l)
#函数名:fun_can_send_OneFrame
#参 数:bus(object),can_id(Number),can_data(list),is_extended_id=False
#返 回:None
#说 明:通过CAN报文DLC<=8时,调用本函数
def fun_can_send_OneFrame(bus,can_id,can_data,is_extended_id=False):
Msg_send = can.Message(extended_id=is_extended_id,arbitration_id=can_id,data=can_data,)
print(Msg_send)
try:
bus.send(Msg_send)
except can.CanError:
pass
#函数名:fun_can_rev_OneFrame
#参 数:总线对象
#返 回:CAN Msg对象
#说 明:CAN报文接收时,首先调用本函数
def fun_can_rev_OneFrame(bus):
try:
while True:
Msg_recv = bus.recv(1)
if Msg_recv is not None:
print(Msg_recv)
break
except can.CanError:
pass
return Msg_recv
#函数名:fun_Get_PCI
#参 数:CAN Msg.data
#返 回:Number
# 0<->单帧
# 1<->首帧
# 2<->连续帧
# 3<->流控帧
def fun_Get_PCI(Msgdata):
PCI = Msgdata[0] & 0xF0
if PCI==0x00: #单帧
PCIFlag = 0
return PCIFlag
elif PCI==0x10: #首帧
PCIFlag = 1
return PCIFlag
elif PCI==0x20: #连续帧
PCIFlag = 2
return PCIFlag
elif PCI==0x30: #流控帧
PCIFlag = 3
return PCIFlag
#函数名:fun_can_send_MultiFrame
#参 数:bus(object),can_id(Number),can_data(list),data_length(Number)
#返 回:None
#说 明:通过CAN报文发送多帧数据时调用本函数,函数默认一次性将多帧数据发出,忽略流控处理
def fun_can_send_MultiFrame(bus,can_id,can_data,data_length):
FirstFrameData = []
if (data_length//256 >= 16):
pass
else:
FirstFrameData[0:0+2] = data_length.to_bytes(2,byteorder='big')
FirstFrameData[0] += 0x10
FirstFrameData[2:2+6] = can_data[0:0+6]
fun_can_send_OneFrame(bus,can_id,FirstFrameData)
FlowControlMsg = fun_can_rev_OneFrame(bus)
if((data_length-6)%7==0):
ConsecutiveFrameCounter = (data_length-6)//7
for i in range(1,ConsecutiveFrameCounter+1,1):
ConsecutiveFrameData=[]
SN = i%16
ConsecutiveFrameData[0:0+1]=(SN+0x20).to_bytes(1,byteorder='big')
ConsecutiveFrameData[1:1+7]=can_data[6+(i-1)*7:6+7*i]
#print(fun_Bytes_To_Hex_InStr(ConsecutiveFrameData))
fun_can_send_OneFrame(bus,can_id,ConsecutiveFrameData)
else:
ConsecutiveFrameCounter = (data_length-6)//7 + 1
remainder = (data_length-6)%7
for i in range(1,ConsecutiveFrameCounter+1,1):
if(i<ConsecutiveFrameCounter):
ConsecutiveFrameData=[]
SN = i%16
ConsecutiveFrameData[0:0+1]=(SN+0x20).to_bytes(1,byteorder='big')
ConsecutiveFrameData[1:1+7]=can_data[6+(i-1)*7:6+7*i]
#print(fun_Bytes_To_Hex_InStr(ConsecutiveFrameData))
fun_can_send_OneFrame(bus,can_id,ConsecutiveFrameData)
else:
ConsecutiveFrameData=[]
SN = i%16
ConsecutiveFrameData[0:0+1]=(SN+0x20).to_bytes(1,byteorder='big')
ConsecutiveFrameData[1:1+remainder]=can_data[data_length-remainder:data_length]
#print(fun_Bytes_To_Hex_InStr(ConsecutiveFrameData))
fun_can_send_OneFrame(bus,can_id,ConsecutiveFrameData)
#print(fun_Bytes_To_Hex_InStr(FirstFrameData))
#函数名:fun_can_recv_MultiFrame
#参 数:总线对象,接收数据长度
#返 回:列表
#说 明:调用该函数前,先调用Get_PCI函数,判断接受的第一帧数据为首帧数据,并将首帧的数据长度参数传入;
# 本函数返回的Data中不包括首帧中的数据
def fun_can_recv_MultiFrame(bus,data_length):
MultiFrame_Recv_Data=[]
RecievedData=[]
if((data_length-6)%7==0):
FrameRecvCnter = (data_length-6)//7
for i in range(0,FrameRecvCnter):
Msg_recv = fun_can_rev_OneFrame(bus)
MultiFrame_Recv_Data.append(Msg_recv.data)
else:
print("Entered CF recieve function...")
FrameRecvCnter = (data_length-6)//7 + 1
for i in range(0,FrameRecvCnter):
Msg_recv = fun_can_rev_OneFrame(bus)
MultiFrame_Recv_Data.append(Msg_recv.data)
FrameNumber = len(MultiFrame_Recv_Data)
if((data_length-6)%7==0): #去除SN保留实际数据
for i in range(0,FrameNumber,1):
RecievedData += MultiFrame_Recv_Data[i][1:1+7]
else:
DataRemainder = (data_length-6)%7
for i in range(0,FrameNumber,1):
if(i<FrameNumber-1):
RecievedData += MultiFrame_Recv_Data[i][1:1+7]
else:
RecievedData += MultiFrame_Recv_Data[i][1:1+remainder]
return RecievedData
if __name__ == '__main__':
data=[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]
data*=9
#print('Data Len is {}:\n {}'.format(len(data),data))
#fun_can_send_data(bus,CAN_ID_TX,data)
#msg1 = can.Message(data=[0x02,2,3])
#PCI = fun_Get_PCI(msg1.data)
#print("PCI = 0x%2X" %PCI)
#fun_can_send_MultiFrame(bus,CAN_ID_TX,data,len(data))
2 安全访问算法实现
#!/usr/bin/env python3
# File Name:SecurityAccessAlgrithm.py
# Author :Patek
# data :2019-02-28
# Note : This script is used to $27 service of UDS.
import hashlib
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
def fun_Bytes_To_Hex_Instr(bytes):
l = [('%2x' %i) for i in bytes]
#print(l)
return "|".join(l)
def fun_Generate_Secret_Seed_Inbytes():
PUN = get_random_bytes(8)
SecLev =bytes([0x01])
SessNum = bytes([0x02])
ECUSeriNum = get_random_bytes(20)
return (PUN+SecLev+SessNum+ECUSeriNum)
def fun_Generate_Secret_Key_Inbytes(PUN_Get_from_Seed_Inbytes):
Fingerprint = get_random_bytes(16)
secretekey =PUN_Get_from_Seed_Inbytes+Fingerprint
return secretekey
def fun_FillTo32ByteInPKCS7(data_In_bytes):
DataLength = len(data_In_bytes)
print("Orignal seed length is :",DataLength)
residue = 16 - (DataLength%16)
list_t = []
for i in range(residue):
list_t.append(residue)
result = data_In_bytes+bytes(list_t)
return result
def fun_ReverseFillTo32ByteInPKCS7(data_In_bytes):
data = data_In_bytes
if data[-1] == 0x01:
return data[:-1]
elif (data[-1] == 0x02 and data[-2]==0x02):
return data[:-2]
elif (data[-1] == 0x03 and data[-2]==0x03 and data[-3]==0x03):
return data[:-3]
elif (data[-1] == 0x04 and data[-2]==0x04 and data[-3]==0x04 and data[-4]==0x04):
return data[:-4]
elif (data[-1] == 0x05 and data[-2]==0x05 and data[-3]==0x05 and data[-4]==0x05 and data[-5]==0x05):
return data[0:-5]
elif (data[-1] == 0x06 and data[-2]==0x06 and data[-3]==0x06 and data[-4]==0x06 and data[-5]==0x06 and data[-6]==0x06):
return data[:-6]
elif (data[-1] == 0x07 and data[-2]==0x07 and data[-3]==0x07 and data[-4]==0x07 and data[-5]==0x07 and data[-6]==0x07 and data[-7]==0x07):
return data[:-7]
elif (data[-1] == 0x08 and data[-2]==0x08 and data[-3]==0x08 and data[-4]==0x08 and data[-5]==0x08 and data[-6]==0x08 and data[-7]==0x08 and data[-8]==0x08):
return data[:-8]
elif (data[-1] == 0x09 and data[-2]==0x09 and data[-3]==0x09 and data[-4]==0x09 and data[-5]==0x09 and data[-6]==0x09 and data[-7]==0x09 and data[-8]==0x09 and data[-9]==0x09):
return data[:-9]
elif (data[-1] == 0x0a and data[-2]==0x0a and data[-3]==0x0a and data[-4]==0x0a and data[-5]==0x0a and data[-6]==0x0a and data[-7]==0x0a and data[-8]==0x0a and data[-9]==0x0a and data[-10]==0x0a):
return data[:-10]
elif (data[-1] == 0x0b and data[-2]==0x0b and data[-3]==0x0b and data[-4]==0x0b and data[-5]==0x0b and data[-6]==0x0b and data[-7]==0x0b and data[-8]==0x0b and data[-9]==0x0b and data[-10]==0x0b and data[-11]==0x0b):
return data[:-11]
elif (data[-1] == 0x0c and data[-2]==0x0c and data[-3]==0x0c and data[-4]==0x0c and data[-5]==0x0c and data[-6]==0x0c and data[-7]==0x0c and data[-8]==0x0c and data[-9]==0x0c and data[-10]==0x0c and data[-11]==0x0c and data[-12]==0x0c):
return data[:-12]
elif (data[-1] == 0x0d and data[-2]==0x0d and data[-3]==0x0d and data[-4]==0x0d and data[-5]==0x0d and data[-6]==0x0d and data[-7]==0x0d and data[-8]==0x0d and data[-9]==0x0d and data[-10]==0x0d and data[-11]==0x0d and data[-12]==0x0d and data[-13]==0x0d):
return data[:-13]
elif (data[-1] == 0x0e and data[-2]==0x0e and data[-3]==0x0e and data[-4]==0x0e and data[-5]==0x0e and data[-6]==0x0e and data[-7]==0x0e and data[-8]==0x0e and data[-9]==0x0e and data[-10]==0x0e and data[-11]==0x0e and data[-12]==0x0e and data[-13]==0x0e and data[-14]==0x0e):
return data[:-14]
elif (data[-1] == 0x0f and data[-2]==0x0f and data[-3]==0x0f and data[-4]==0x0f and data[-5]==0x0f and data[-6]==0x0f and data[-7]==0x0f and data[-8]==0x0f and data[-9]==0x0f and data[-10]==0x0f and data[-11]==0x0f and data[-12]==0x0f and data[-13]==0x0f and data[-14]==0x0f and data[-15]==0x0f):
return data[:-15]
def SHA1_Hash(Input_In_bytes):
SHA1 = hashlib.sha1()
SHA1.update(Input_In_bytes)
digest = SHA1.digest()
print("Digest calculated expressed in hex value is :\n",SHA1.hexdigest().upper())
return digest
def fun_Get_Encrypt_Data_byAESCBC(key_bytes,encrypt_mode,initial_vector_bytes,iput_data_after_padding_bytes):
cipher = AES.new(key_bytes, encrypt_mode, initial_vector_bytes)
ct_bytes = cipher.encrypt(iput_data_after_padding_bytes)
return ct_bytes
def fun_Get_Decrypt_Data_byAESCBC(key_bytes,encrypt_mode,initial_vector_bytes,cipher_data_bytes):
cipher = AES.new(key_bytes, encrypt_mode, initial_vector_bytes)
pt_bytes = cipher.decrypt(cipher_data_bytes)
return pt_bytes
if __name__ == '__main__':
#seed = bytes([0xDA,0xAD,0xBC,0xDA,0x21,0x28,0xC1,0x5C,0xB1,0xA0,0xB0,0xFC,0xCC,0xBB,0xCD,0xCA,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18])
seed = get_random_bytes(30)
#key = bytes([0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f])
statickey = bytes([0xDA,0xAD,0xBC,0xDA,0x21,0x28,0xC1,0x5C,0xB1,0xA0,0xB0,0xFC,0xCC,0xBB,0xCD,0xCA])
iv = bytes([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
#print("Original seed in bytes type is :\n",seed)
print("Expressed by hex value,the original data of seed is :\n",fun_Bytes_To_Hex_Instr(seed))
SHA1Digest = SHA1_Hash(seed)
#print("Digest calculated from seed expressed in bytes type is :\n",SHA1Digest)
FilledData = fun_FillTo32ByteInPKCS7(seed)
#print("After filled according PKCS7 standar the seed expressed in bytes type is:\n",FilledData)
print("After filled according PKCS7 standar the seed expressed in hex type is:\n",fun_Bytes_To_Hex_Instr(FilledData))
print("\n-----------------------Encrypt the filled seed in PKCS7 standar with AES128-CBC algrithm------------------------\n")
cipther_text = fun_Get_Encrypt_Data_byAESCBC(statickey,AES.MODE_CBC,iv,FilledData)
print("The cipher data is:\n",fun_Bytes_To_Hex_Instr(cipther_text))
print("\n---------------------------Decrypt the cipher data in with AES128-CBC algrithm----------------------------------\n")
plain_text = fun_Get_Decrypt_Data_byAESCBC(statickey,AES.MODE_CBC,iv,cipther_text)
print("The plain data is:\n",fun_Bytes_To_Hex_Instr(plain_text))
print("\n-----------------------------Detach plain data according to PKCS7 standard--------------------------------------\n")
plain_text_detached = fun_ReverseFillTo32ByteInPKCS7(plain_text)
print("The plain data detached is:\n",fun_Bytes_To_Hex_Instr(plain_text_detached))
#print("Test for generated secrete seed data type:\n",fun_Generate_Secret_Seed_Inbytes())
#print("Test for generated secrete seed data expressed in hex:\n",fun_Bytes_To_Hex_Instr(fun_Generate_Secret_Seed_Inbytes()))
#attached = bytes([0x8,0xb6,0xf1,0x87,0x35,0x32,0x38,0x82,0x78,0x91,0x89,0x35,0x0e,0xb6,0x68,0x1b,0xb9,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f])
#detached = fun_ReverseFillTo32ByteInPKCS7(attached)
#print(fun_Bytes_To_Hex_Instr(detached))
3 应用层
#!/usr/bin/env python3
# FileName:UDS_$27.py
# Author: Patek
# Data : 2019-03-04
# Note : This script is used to $27 service of UDS
import can
import SecurityAccessAlgrithm as SA
import UDSTransLayer as Transmitter
from Crypto.Cipher import AES
#bus初始化配置
bus = can.interface.Bus(bustype='neovi', channel=1, bitrate=500000)
CAN_ID_TX=0x7F8
CAN_ID_RX=0x7F0
can_filters = [
{"can_id": 0x7F0, "can_mask": 0x7FF, "extended": False},
] #报文过滤器设置,接受CAN ID为0x7F8的CAN报文
bus.set_filters(can_filters)
Statickey = bytes([0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F])
iv = bytes([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
FingerPrint = bytes([0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F])
while True:
print('Script is on running for recieving can data.....')
Msg = Transmitter.fun_can_rev_OneFrame(bus)
print('Recieved a message.....')
if(1 == Transmitter.fun_Get_PCI(Msg.data)):
if(Msg.data[2]==0x27 and Msg.data[3]==0x03): #send seed 27 03
print('Data recieving....\n') #return key 27 04
RecvData=[]
RecvData += Msg.data
print("After recieve the first frame the buffered data in RecvData is :\n", SA.fun_Bytes_To_Hex_Instr(RecvData))
RecvDataLength = ((Msg.data[0] &0x0F) << 8) | Msg.data[1]
print("The data need to be received during the consecutive frame is :%d \n" %RecvDataLength)
FlowControlData = [0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
Transmitter.fun_can_send_OneFrame(bus,CAN_ID_TX,FlowControlData) #对首帧回复流控帧
print("FlowControl frame has been sent....\n")
RecvData += Transmitter.fun_can_recv_MultiFrame(bus,RecvDataLength)
print("RecvData is:\n ",SA.fun_Bytes_To_Hex_Instr(bytes(RecvData)))
SecuritySeed = RecvData[4:4+RecvDataLength-2]
print("SecuritySeed is:\n ",SA.fun_Bytes_To_Hex_Instr(SecuritySeed))
SecretSeed = SA.fun_Get_Decrypt_Data_byAESCBC(Statickey,AES.MODE_CBC,iv,bytes(SecuritySeed))[0:0+30]
print("SecretSeed is:\n ",SA.fun_Bytes_To_Hex_Instr(SecretSeed))
Sessionkey = SA.SHA1_Hash(SecretSeed)[0:0+16]
print("Sessionkey is:\n ",SA.fun_Bytes_To_Hex_Instr(Sessionkey))
SecretKey = SecretSeed[0:0+8]+FingerPrint
print("Secretkey is:\n ",SA.fun_Bytes_To_Hex_Instr(SecretKey))
Secretkey_Filled = SA.fun_FillTo32ByteInPKCS7(SecretKey)
print("Secretkey_Filled is:\n ",SA.fun_Bytes_To_Hex_Instr(Secretkey_Filled))
SecurityKey=SA.fun_Get_Encrypt_Data_byAESCBC(Sessionkey,AES.MODE_CBC,iv,Secretkey_Filled)
print("SecurityKey is:\n ",SA.fun_Bytes_To_Hex_Instr(SecurityKey))
SecurityKey_send = bytes([0x27,0x04])
SecurityKey_send += SecurityKey
Transmitter.fun_can_send_MultiFrame(bus,CAN_ID_TX,SecurityKey_send,len(SecurityKey_send))
else:
pass
else:
pass