sha1加密驱动

来源:互联网 发布:头北脚南睡觉好吗知乎 编辑:程序博客网 时间:2024/05/16 01:08

概述

    本文档介绍DS28E01-100的加密驱动,该加密驱动支持x86ppc硬件环境,同时可以在linuxvxworks上运行。DS28E01使用1--WIRE总线,只需要一个引脚就可以完成读写数据和同步时钟的功能。

用户接口

2.1软件环境的不同

    本由于操作系统的不同,在具体操作上有所差异,在源码里提供了license.h文件,可以在里面定义宏定义来标识使用的环境。其中LICENSE_LINUX在我们使用的是linux系统时需要定义它;如果是vxworks系统,则定义LICENSE_VXWORKS宏定义。

2.2留给用户的接口

对用户只要加载我们的库文件,就可以调用 c3itop_check_codon( void * p1, void * p2 , void *func),当然需要先申请三个指针,其中p1标识校验密码成功,而p2标志着校验失败。

这个函数里调用了两个函数,分别用来初始化硬件和校验密钥。

关于校验密钥在第三章的校验密钥中有描述,下节描述硬件的初始化。

2.2硬件的初始化

针对vxworks平台,DS28E01加密芯片挂接在显卡上,所以需要先调用pciConfigInLong(b, d, f, 0x18, &memio_addr)获取io映射地址,并且经过偏移得到对应的gpio data寄存器和gpio

Control寄存器。control寄存器可以将gpio设置成输入或输出。

    而对于ppclinux平台,DS28E01连接到cpu的一个管脚上,需要控制mmap GPIO寄存器,然后根据偏移得到控制寄存器,数据寄存器和上拉寄存器。

    

密钥的写入和校验

3.1写入密钥

     unique:用于指定参与到SHA-1运算中的64位密码的特性。

     anoy:当为DISABLE时器件的64ROM ID参与到SHA-1运算中。否则64ROM ID8个字节的0xFF取代输入至SHA-1 buffer

      对于unique,我们取disnableanoy则取enable;即始终不使用rom id参与密码生成和计算;

3.1.1初始化

    要写入密钥,首先要初始化密钥,并且初始化要使用的指令和地址等变量;

    1.初始化TA1TA2,即地址寄存器;

    2.获取rom id,这是一个出厂已经固化好的,我们只要读出来并校验就行了。getromid()有两个功能,首先读取密钥并且赋值给全局变量ROMID数组,方便我们使用;第二个功能就是检验,判断接受到的数据和发送数据是否一致。这里使用的是crc8校验。关于64位密钥的校验,则使用crc16位校验。

    3.将我们的密钥赋值给devicessecret;

3.1.2写密钥

      接下来就要将密钥写入暂存区。为什么要先写入暂存区而不是密码存储区,这主要是为了防止密钥传输过程中出错。现在来分析下writesc()这个函数:

databuffer[i++] = Wrscratch;

databuffer[i++] = TA1;

databuffer[i++] = TA2;

进行初始化,将写暂存区和两个地址寄存器地址拷贝给databuffer,然后将密钥赋值给databuffer:

for (j = 0; j <= (i - 1); j++) {

ow_writebyte(databuffer[j]);

}

然后读取校验码:

databuffer[i++] = ow_readbyte(); /* Read back first byte of CRC16. */

databuffer[i] = ow_readbyte(); /* Read back second byte of CRC16. */

接下来进行校验,判断校验值;

3.1.3校验和load

    写入rom命令和读取命令,然后读回13个字节,进行校验;最后将数据load到密码区域。最后清零暂存区,执行copy暂存区后执行refresh暂存区,可减少失效弱位的位数。

3.2校验密钥

3.2.1前期准备

    首先将头两个字节设置成0x00,然后加5个随机数,最后再写一个0x00,凑够8个数;

然后将这些数据写入。同时读取缓存数据,看是否正确,这是为后面的hash校验做准备。

随后设置romid为0xff,这个romid在hash校验算法中会用到,写入Anautpage命令 ,读取数据并校验。

前期的工作主要是为hash算法做准备,为hash算法的实现准备数据。

3.2.2hash算法校验

1.前期初始化

memcpy(SHAVM_Message, devicesecret, 4); /* Copy the devicesecret to input buffer. */

memcpy(&SHAVM_Message[4], &pageback[3], 32); /* Fill 32 bytes page data to input buffer. */

memcpy(&SHAVM_Message[36], &subpbuffer[2] , 2); /* Set 2 bytes of challenge to input buffer. */

SHAVM_Message[38] = 0xFF;

SHAVM_Message[39] = 0xFF;

SHAVM_Message[40] = 0x40 | TA1 >> 5; /* Set MPX. */ 

memcpy(&SHAVM_Message[41], ROMID, 7); /* Input ROMID */

memcpy(&SHAVM_Message[48], &devicesecret[4], 4);/* Input another 4 bytes of basic secret to input buffer. */

memcpy(&SHAVM_Message[52], &subpbuffer[4], 3); /* Set the input buffer as defined by datasheet. */

SHAVM_Message[55] = 0x80; /* Set the input buffer as defined by datasheet. */ 

    memset(&SHAVM_Message[56], 0x00, 6); /* Set the input buffer as defined by datasheet. */ 

    SHAVM_Message[62] = 0x01; /* Set the input buffer as defined by datasheet. */ 

    SHAVM_Message[63] = 0xB8;

for (i = 0; i <= 7; i++) {

devicesecret[i] = 0x00;

}

先来看一下hash算法都用了哪些数据:

(1)先写4个密钥,将其他4个字节的密钥写到第48字节;

(2)Anautpage写入和读取的数据;

(3)5个我们在第一步生成的随机数;

(4)0x40|TA1>>5

(5)然后写入一些数据手册规定的常数;

2.hash算法

     hash算法的代码和原理在后面hash算法章节会有所描述,这里通过该算法得到8位mac地址,并且和ds28e01返回的数据进行比较,如果比较成功,则数据校验正确。

HASH算法

4.1基本概念

对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。 SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。

SHA1算法中,我们必须把原始消息(字符串,文件等)转换成位字符串。SHA1算法只接受位作为输入。假设我们对字符串“abc”产生消息摘要。首先,我们将它转换成位字符串如下:

01100001 01100010 01100011

―――――――――――――

‘a’=97 ‘b’=98 ‘c’=99

这个位字符串的长度为24。下面我们需要5个步骤来计算消息摘要MAC

4.1.1 补位

消息必须进行补位,以使其长度在对512取模以后的余数是448。也就是说,(补位后的消息长度)%512 = 448。即使长度已经满足对512取模后余数是448,补位也必须要进行。

补位是这样进行的:先补一个1,然后再补0,直到长度满足对512取模后余数是448。总而言之,补位是至少补一位,最多补512位。还是以前面的“abc”为例显示补位的过程。

原始信息: 01100001 01100010 01100011

补位第一步:01100001 01100010 01100011 1

首先补一个“1”

补位第二步:01100001 01100010 01100011 10…..0

然后补423“0”

我们可以把最后补位完成后的数据用16进制写成下面的样子

61626380 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000

现在,数据的长度是448了,我们可以进行下一步操作。

4.1.2补长度

所谓的补长度是将原始数据的长度补到已经进行了补位操作的消息后面。通常用一个64位的数据来表示原始消息的长度。如果消息长度不大于2^64,那么第一个字就是0。在进行了补长度的操作以后,整个消息就变成下面这样了(16进制格式)

61626380 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000018

如果原始的消息长度超过了512,我们需要将它补成512的倍数。然后我们把整个消息分成一个一个512位的数据块,分别处理每一个数据块,从而得到消息摘要。

4.2算法

4.2.1 使用的常量

一系列的常量字K(0), K(1), ... , K(79),如果以16进制给出。它们如下:

Kt = 0x5A827999 (0 <= t <= 19)

Kt = 0x6ED9EBA1 (20 <= t <= 39)

Kt = 0x8F1BBCDC (40 <= t <= 59)

Kt = 0xCA62C1D6 (60 <= t <= 79).

4.2.1需要使用的函数

SHA1中我们需要一系列的函数。每个函数ft (0 <= t <= 79)都操作32位字BCD并且产生32位字作为输出。ft(B,C,D)可以如下定义

ft(B,C,D) = (B AND C) or ((NOT B) AND D) ( 0 <= t <= 19)

ft(B,C,D) = B XOR C XOR D (20 <= t <= 39)

ft(B,C,D) = (B AND C) or (B AND D) or (C AND D) (40 <= t <= 59)

ft(B,C,D) = B XOR C XOR D (60 <= t <= 79).

4.2.2 计算消息摘要

必须使用进行了补位和补长度后的消息来计算消息摘要。计算需要两个缓冲区,每个都由532位的字组成,还需要一个8032位字的缓冲区。第一个5个字的缓冲区被标识为ABCDE。第二个5个字的缓冲区被标识为H0, H1, H2, H3, H4

80个字的缓冲区被标识为W0, W1,..., W79

另外还需要一个一个字的TEMP缓冲区。

为了产生消息摘要,在第4部分中定义的16个字的数据块M1, M2,..., Mn

会依次进行处理,处理每个数据块Mi 包含80个步骤。

在处理每个数据块之前,缓冲区{Hi} 被初始化为下面的值(16进制)

H0 = 0x67452301

H1 = 0xEFCDAB89

H2 = 0x98BADCFE

H3 = 0x10325476

H4 = 0xC3D2E1F0.

现在开始处理M1, M2, ... , Mn。为了处理 Mi,需要进行下面的步骤

(1). 将 Mi 分成 16 个字 W0, W1, ... , W15, W0 是最左边的字

(2). 对于 t = 16 到 79 令 Wt = S1(Wt-3 XOR Wt-8 XOR Wt- 14 XOR Wt-16).

(3). 令 A = H0, B = H1, C = H2, D = H3, E = H4.

(4) 对于 t = 0 到 79,执行下面的循环

TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt;

E = D; D = C; C = S30(B); B = A; A = TEMP;

(5). 令 H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E.

在处理完所有的 Mn, 后,消息摘要是一个160位的字符串

.1--WIRE总线

5.15.  

5.1 DS28E01硬件分析

1.DS28E01:1024e2prom分四页;又称数据存储器。

2.64位暂存器;在写入数据存储器和密钥存储器之前,先写入暂存器中,检验后存入;

3.唯一的64ROM注册码,由工厂激活刻入芯片;前8位是1-wire家族码,中间48位是

  唯一的序列号,后8位是前56位的循环冗余检验码(CRC)

4.64位密钥存储器;

5.内置512SHA-1引擎,用于计算160位信息认证码(MAC)并生成密钥;

7.寄存器页:

     TA1TA2存放写入或读取数据的目标地址;E/S用于验证数据完整性;TA1低三位始终为0,因为只接受8字节数据块;E/S低三位始终为1E/S5位说明数据有误;第346位没有功能,读出数据始终位1


5.2 1--WIRE基本操作

1-wire总线的基本操作分为复位,读和写三种,所有读写操作都是低位在前高位在后。

1.复位

在每次总线通信之前主机必须首先发送复位信号。

产生复位信号时主机首先将总线拉低480960μs然后释放。1-Wire总线器件在接收到有效跳变的1560μs内会将总线拉低60240μs,在此期间主机可以通过对DQ采样来判断是否有从器件挂接在当前总线上。

1.函数Reset()的返回值为0表示有器件挂接在总线上,返回值为1表示没有器件挂接在总线上。

uchar Reset(void)

uchar tdq;
DQ=0; //主机拉低总线
delay480μs(); //等待480μs
DQ=1; //主机释放总线
delay60μs(); //等待60μs
tdq=DQ; //主机对总线采样
delay480μs(); //等待复位结束
return tdq; //返回采样值
}

2.写操作

1-Wire总线写1bit至少需要60μs,同时还要保证两次连续的写操作有1μs以上的间隔。

若待写位wbit0则主机拉低总线60μs然后释放,写0操作完成。若待写位wbit1,则主机拉低总线并在115μs内释放,然后等待60μs,写1操作完成。

2.

void Writebit(uchar wbit)

_nop_(); 
//保证两次写操作间隔1μs以上
DQ=0;
_nop_(); 
//保证主机拉低总线1μs以上
if(wbit)

//向总线写1
DQ=1;
delay60μs();
}
else 

//向总线写0
delay60μs();
DQ=1;
}
}

3.读操作

1-Wire总线读取1bit同样至少需要60μs,同时也要保证两次连续的读操作间隔1μs以上。

3.从总线读数据时,主机首先拉低总线1μs以上然后释放,在释放总线后的115μs内主机对总线的采样值即为读取到的数据。

uchar Readbit()

uchar tdq;
_nop_(); 
//保证两次连续写操作间隔1μs以上
DQ=0;
_nop_(); 
//保证拉低总线的时间不少于1μs
DQ=1;
_nop_();
tdq=DQ; 
//主机对总线采样
delay60μs();
//等待读操作结束
return tdq; 
//返回读取到的数据
}

5.3 ROM命令

DS28E01内部光刻了一个长度为64bitROM编码,这个编码是器件的身份识别标志。当总线上挂接着多个DS28E01时可以通过ROM编码对特定器件进行操作。ROM功能命令是针对器件的ROM编码进行操作的命令,共有7个,长度均为8bit1Byte)。这里介绍我们用到的。

①读ROM(33H)

当挂接在总线上的1-Wire总线器件接收到此命令时,会在主机读操作的配合下将自身的ROM编码按由低位到高位的顺序依次发送给主机。总线上挂接有多个时,此命令会使所有器件同时向主机传送自身的ROM编码,这将导致数据的冲突。

②匹配ROM(55H)

主机在发送完此命令后,必须紧接着发送一个64bitROM编码,与此ROM编码匹配的从器件会响应主机的后续命令,而其他从器件则处于等待状态。该命令主要用于选择总线上的特定器件进行访问。

③跳过ROM(CCH)

发送此命令后,主机不必提供ROM编码即可对从器件进行访问。与读ROM命令类似,该命令同样只适用于单节点的1-Wire总线系统,当总线上有多个器件挂接时会引起数据的冲突。

④查找ROM(F0H)

当主机不知道总线上器件的ROM编码时,可以使用此命令并配合特定的算法查找出总线上从器件的数量和各个从器件的ROM编码。

5.4DS28E01操作及特殊命令
    1-Wire总线相关的命令分为ROM功能命令和器件功能命令两种,ROM功能命令具有通用性,不仅适用于DS28E01也适用于其他具有1-Wire总线接口的器件,主要用于器件的识别与寻址;器件功能命令具有专用性,它们与器件的具体功能紧密相关。下面是DS28E01的器件功能命令。

#define Wrscratch 0x0F  //写到暂存区

#define Rescratch     0xAA     //读取暂存区

#define Coscratch     0x55     //copy暂存区

#define Redmemory 0xF0     //读取E2PROM,即数据存储区

#define Loadfirse 0x5A         //将数据load到密钥存储区

#define Comnextse 0x33     

#define Reautpage 0xA5    //把Reautpage命令在进行身份验证,anoydisable

#define Anautpage 0xCC    //同上,只是anoyenable,即不实用rom id

#define Refreshsc 0xA3

6. 关于延时

为了减少linuxvxworks中延时的误差可能超过DS28E01所能接受的范围,我们使用的delay函数是用for循环,这个for循环是通过实验和自己计算出来的,我们这里介绍实验

延时函数的时间时使用到的函数,当然,在linuxvxworks中是不一样的。

vxworks中的测试函数:

  1 #include <stdio.h>

  2 

  3 int test(void)

  4 {

  5     int Count=0;

  6     int i;

  7     int  FirstTsc = pentiumTscGet64();

  8     for(i=0;i<383;i++)

  9         ;

 10     int SecondTsc = pentiumTscGet64();

 11     Count = SecondTsc-FirstTsc;

 12     printf("Count is %d\n",Count);

 13     return 0;

 14 }

linux中的测试函数:

#include <stdio.h>
#include <sys/time.h>
#define MILLION 1000000L

void function_to_time(void);//要测试的函数

int main(void){
long timedif;
struct timeval tpend; //timeval结构体包含两个成员:time_t tv_sec;Epoch开始的秒数 time_t tv_usec;Epoch开始的微秒数
struct timeval tpstart;

if(gettimeofday(&tpstart, NULL)){
printf(stderr, "Failed to get start time\n");
return 1;
}

function_to_time();

if(gettimeofday(&tpend, NULL)){
printf(stderr, "Failed to get end time\n");
return 1;
}

timedif = MILLION*(tpend.tv_sec - tpstart.tv_sec) + tpend.tv_usec - tpstart.tv_usec;
printf("The function_to_time took %ld microseconds\n", timedif);
return 0;
}

原创粉丝点击