引言
为了保护计算机系统的安全,有众多的解决方案,比如使用加密软件对敏感数据进行保护。加密软件自身的可信性缺乏保障,不能提供根本的安全保护。USB密钥等硬件令牌难于和平台集成到一起并对平台的运行状况进行度量[1]。TCG(Trusted Computing Group)技术委员会提出的TPM设计规范可以控制系统从启动到运行的全过程。TPM可以安全地记录系统运行的所有软件的信息,用户可以根据该信息判断当前运行环境是否可信,某些环节是否出现安全问题[2],是提高计算机系统安全性的有效途径。随着嵌入式系统的广泛应用,其面对的硬件和软件的攻击也与日俱增[3]。可以将TCG的可信计算理念融入到嵌入式平台的设计中,以增强嵌入式平台的安全性。目前的TPM产品大多通过LPC(Low Pin Count)或SMBus(System Management Bus)与PC平台连接,而在嵌入式平台上通过这两种接口扩展TPM不是最适合的方式。本文在参考TCG相关规范的基础上设计了一种通过I2C接口扩展TPM的嵌入式平台可信设计方案,实现了嵌入式平台上基本的可信计算。
1 嵌入式可信计算平台TPM接口扩展
本文研究的嵌入式可信计算平台采用的是Samsung公司推出的16/32位RISC处理器S3C2410A。S3C2410A采用了ARM920T内核,片内资源丰富,可以适应不同层次的开发,主要面向手持设备以及高性价比、低功耗的应用[4]。S3C2410A内部集成了I2C总线接口,可进行8位、双向串行数据传输。标准模式下数据传输速度为100 kb/s,快速模式下可达400 kb/s。
参考文献[5]使用GPIO模拟LPC接口的方式扩展TPM,至少占用了10个GPIO。这种基于软件模拟的方式会增加可信引导程序的复杂程度。同时由于GPIO大多是多功能引脚,如果被占用,会影响嵌入式平台相应功能的工作。参考文献[6]使用SMBus在嵌入式可信终端上扩展TPM。SMBus是在I2C总线的基础上开发出的一种总线技术,但是两种规范之间在电气、时序、协议和操作模式等方面有些区别[7]。在使用时需要在I2C总线的驱动代码中将这些参数进行修改,从而模拟成SMBus总线。TPM的一个重要功能是度量代码的完整性,需要将目标代码发送到TPM,调用TPM内部的SHA1散列算法引擎进行度量。通信接口的速度会直接影响到TPM的工作效率,而SMBus的最高数据传输率只有100 kb/s。
AT97SC3204T是Atmel公司推出的一款专为嵌入式系统设计的TPM,遵循TPM1.2规范,内置硬件随机数发生器、RSA非对称密钥引擎、SHA1散列算法引擎、HMAC计算引擎等,通过TWI总线进行通信。TWI总线是对I2C总线的继承和发展,接口时序和I2C总线兼容,TWI传输的快速模式为400 kb/s。因此本研究采用I2C总线扩展拥有TWI接口的TPM芯片。
扩展了TPM的嵌入式平台以S3C2410A,AT97SC3204T等芯片为核心,拥有TFT触摸屏、USB设备接口,配备10M速率网卡。容量为64 MB的NAND Flash用于存储Bootloader、内核和文件系统。USB接口可以同时连接U盘和键盘。嵌入式可信计算平台功能框图如图1所示。
图1 嵌入式可信计算平台功能框图
2 嵌入式平台下TPM驱动设计
可信启动的关键是信任链的建立。在本嵌入式平台上,信任是从Bootloader的第一阶段的可信代码开始的,在把控制权交给下一段代码之前,这段可信代码需要度量下一段将要执行的代码,因此要求TPM在嵌入式平台启动的时候启动并开始工作。所以在Bootloader中需要启动TPM,对代码进行度量,并将结果扩展到PCR中。由于Bootloader和Linux内核程序的运行环境不同,需要对两个阶段分别设计驱动程序。
2.1 TPM命令格式及使用方法
TPM是一个从设备,无法自主地工作。必须由外部的设备先发送命令,TPM才会按照命令工作,并返回相应数据。TCG对TPM命令做了规范[8],每条命令都至少有10个字节。前两个字节是TPM读写标志,接着4个字节是整个数据包的字节数,剩余字节的前4个字节是命令序号。TPM不支持主机在没有读取实际数据的前提下获得相应数据的长度。按照TCG的规范,所有的TPM操作命令运行成功的返回值的字节数都是可以计算出的,但是运行失败时的返回值的字节数可能为不同的数值,而且只有收到返回值后才能判断命令是否运行成功。因此在获取TPM命令返回值时,先接收前6字节数据。根据第5和第6字节的数据计算出返回值的总字节数,再接收剩余的返回值。
在接收到TPM操作命令后,TPM内部的微处理器开始执行操作命令。操作命令的执行时间是不确定的,尤其是一些加密操作的时间最长达到两分钟。TPM在执行当前操作命令时,不会响应新的操作命令,但是会返回一个NACK信号,即第9位为逻辑1。一般TPM不支持中断方式,所以必须通过轮询的方式向TPM发送读地址,检测返回的状态,以判断TPM是否可以响应新的操作命令。
接通电源只是TPM工作的必要条件,TPM在初始化后才能工作。TPM_StartUp命令是TPM的初始化命令,也是确定TPM状态的方法之一。因此TPM_StartUp命令是主机系统上电启动或从休眠状态恢复后向TPM发送的第一个命令。Bootloader或者CRTM根据命令的返回值确定TPM的状态。TPM收到TPM_StartUp命令后,进行自检并启动。如果需要TPM进入完全操作状态,还必须发送TPM_ContinueSelfTest命令。
2.2 Bootloader中TPM驱动设计
Bootloader中编写TPM驱动,主要工作是编写I2C驱动,需要对直接操作底层硬件。S3C2410A中大部分I/O端口都是复用的,因此在使用前需要决定每个I/O端口使用哪种功能。S3C2410A的I2C接口引脚IICSCL和IICSDA分别于GPE14和GPE15复用。需要将地址为0x56000040的端口控制寄存器配置为0xa000。
然后配置与I2C总线接口有关的寄存器,包括:
① 地址寄存器(IICADD)。设置S3C2410A的从机地址。当串行输出使能为0时,IICADD是写使能。无论当前串行输出使能位是什么,IICADD的值都可以在任意时刻读取。在本平台中,S3C2410A始终是主设备。
② 控制寄存器(IICCON)。包括使能ACK,设置I2C总线传输时钟分频比,使能中断,设置发送时钟值。
③ 收发数据移位寄存器(IICDS)。当在IICSTAT寄存器中的串行输出使能位为1时,IICDS是写使能。无论当前串行输出使能位是什么,IICDS的值都可以在任意时刻读取。
④ 状态寄存器(IICSTAT)。主要包括设置总线收发模式,设置串行输出使能位等。本平台中S3C2410A只工作在主机发送模式和主机接收模式。
AT97SC3204T的TWI接口已经是被配置成从机模式的。当TPM的空闲状态一直保持到主机发送8位的TWI从机地址。地址的前7位是固定值0101001(0x29),第8为是读写标志位(读为1,写为0)。如果TPM确认这是一个有效的地址,将在第9位时把SDA电平拉低(ACK)。所以对TPM写操作时,对IICADD赋值0x52;对TPM读操作时,对IICADD赋值0x53。图2描述了向TPM发送操作命令并获取返回值的流程。
图2 向TPM发送操作命令并获取返回值的流程
2.3 Linux下TPM驱动设计
Linux从2.6.12版本开始增加了TPM芯片的驱动程序。由于嵌入式平台扩展TPM的方式与PC有异,该驱动不能直接在嵌入式平台上使用。Linux内核中已经包括了完善的S3C2410A的I2C设备驱动程序。借助内核中的I2C驱动,可以方便地对I2C设备进行读写操作,简化TPM通信程序。I2C设备驱动程序提供了read()、write()和ioctl()等接口,应用层程序通过这些接口访问I2C设备。函数ioctl()可以实现所有的I2C数据格式和算法,本文将采用本接口。函数ioctl()的重要入口参数是i2c_rdwr_ioctl_data结构体,成员包括nmsgs和*msgs。nmsgs定义了I2C信号的开始信号数,*msgs指向i2c_msgs结构体。i2c_msgs结构体的成员及定义如表1所列。
应用层TPM通信的具体过程如下:
① 打开I2C设备。制作文件系统时,在/dev文件夹下建立I2C设备节点,主设备号为89,次设备号为0。打开该设备,并设置模式为可读可写。
② 通过ioctl()函数,设置I2C设备的超时时间和重试次数。
③ 调用tpm_write(unsigned char *wp)函数,入口参数为指向发送数据数组的指针。对i2c_rdwr_ioctl_data结构体成员变量依次赋值。
msgs.len=*(wp+4)*256+*(wp+5);
msgs.addr=0x29;
msgs.flags=0;
msgs.buf=wp;
然后调用ioctl()函数,便可将数据发送到TPM。
④ 调用tpm_read(unsigned char *rp)函数,入口参数为指向接收数据数组的指针。不同的命令或者相同命令的不同执行结果的字节数不尽相同,为了能够接收到全部的数据,接收数据的长度应当设置为AT97SC3204T的输出缓冲区的字节数。对i2c_rdwr_ioctl_data结构体成员变量依次赋值。
msgs.len=1024;
msgs.addr=0x29;
msgs.flags=1;
msgs.buf=rp;
然后调用ioctl()函数,便可从TPM中读取到最后一条命令的返回数据。返回的数据存储在指针rp指向的数组中,有效数据的字节数为*(rp+4)*256+*(rp+5),之后的数据全为0xFF。
3 实验与结果分析
本实验采用韩国Mizi公司的vivi作为Bootloader,Linux内核版本为2.6.32.3,主要验证TPM及其驱动程序的工作状态。具体步骤如下:
① 在Bootloader中加入TPM启动代码,即在平台上电之初向TPM发送TPM_StartUp命令,并读取返回值,若返回值中的TPM_RESULT全为0,表明TPM启动成功。TPM启动成功后,向终端输出OK,并继续Bootloader的启动。
② 在Bootloader中加入SHA1操作,并将度量结果
表1 i2c_msgs结构体的成员及定义
扩展到PCR。安全启动的实现不属于本文的研究范围,此处只做功能性验证。本次是对0x01,0x02,…,0x10等16字节的十六进制数据做SHA1运算,然后将结果扩展到PCR8。由于字节数少于64,只需要依次向TPM发送TPM_SHA1Start,TPM_SHA1CompleteExtend两个命令;
③ 在Linux内核启动后,通过调用内核I2C驱动的方式读取PCR8的值,即向TPM发送TPM_PCRRead命令,并读取返回值。
图3为系统启动时输出到超级终端的信息截图,表明TPM已经成功启动。
图3 Bootloader启动信息截图
图4是读取PCR8的结果。把步骤②的数据做SHA1运算的结果接到20字节的0x00之后,再做SHA1运算,最终结果是图4最后两行数据。这组数据与PC上运算的结果相同。这一实验结果
图4 PCR8的读取结果
表明通过I2C接口扩展的TPM及其驱动程序已经在本嵌入式平台上正常工作。
结语
嵌入式系统的广泛应用和自身安全性的缺陷,使其面临着严峻的安全挑战。本文对现有可信计算技术进行了研究分析,选择了扩展TPM作为解决嵌入式平台安全性问题的方式。通过多种TPM接口分析,实现了在S3C2410A平台上使用I2C接口对TPM进行扩展。最后分析了TPM的基本操作流程。在嵌入式平台上扩展TPM芯片可以从硬件和软件两个方面保障嵌入式系统的安全,实现了基本的可信计算。