TPM emulator 调研报告

TPM emulator 调研报告

1.TPMemulator设计概要

    TPM emulator包包含3个主要部分(见图1):实现真实TPM模拟器的用户空间守护程序(tpmd),作为连接模拟器常规接口的TPM设备驱动库(tddl),可以为与TPM设备驱动的底层兼容提供字符设备的内核模块(tpmd_dev)。


图1:TPMemulator包概览

    本调研基于http://tpm-emulator.berlios.de/发布的tpm_emulator-0.7.4.tar.gz代码进行分析。

2.启动过程

    TPMemulator的启动调用tpm_emulator_init,最初初始化加载外部函数与数据(initialize externalfunctions and data),调用tpm_extern_init(),之后进入自身的初始化(initialize the emulator),首先尝试加载(永久性)存储器中的原有数据,如果失败则采用默认数据值(tryto restore data, if it fails use default values),然后执行自身的初始化函数TPM_Init(startup)。

    TPMemulator自身的初始化分三步进行:第一步,所有数据元素被置为他们的默认值;第二步,对所有密码功能进行内部自检,如果有一个检查测试失败,模拟器进入失败-停止模式(fail-stopmode),并且向所有命令调用返回一条相应的错误消息;第三步,如果所有检查测试顺利通过,模拟器运行指定的启动模式,并且剩余的内部数据被相应的进行初始化。在启动模式的某特殊情况下,将保存长久性数据存储文件中的所有长久性数据的恢复。无论何时,只要对之前存储状态的恢复失败,模拟器也将转入失败-停止状态。

    本部分代码主要位于tpm/tpm_startup.c中。

2.1第一步

    所有数据元素被置为他们的默认值:

tpmData.stany.flags.postInitialise =TRUE;

其中数据结构为:

//These flags reset on anyTPM_Startup command.
typedef struct tdTPM_STANY_FLAGS{
    TPM_STRUCTURE_TAG tag;
    BOOL postInitialise;
    TPM_MODIFIER_INDICATOR localityModifier;
    BOOL transportExclusive;
    BOOL TOSPresent;
} TPM_STANY_FLAGS;

2.2第二步

    对所有密码功能进行内部自检:

TPM_SelfTestFull();

    该步骤分别对内部的随机数生成器(tpm_test_prng())、SHA-1生成器(tpm_test_sha1())、HMAC-SHA-1生成器(tpm_test_hmac())、RSA密钥对生成器(tpm_test_rsa_EK())进行测试,只有当全部成功通过测试后,置位

tpmData.permanent.flags.selfTestSucceeded =TRUE;

否则为FALSE,并将返回结果。

2.3第三步

    如果所有检查测试顺利通过,模拟器运行指定的启动模式:

TPM_Startup(startupType);

    此后根据不同的启动状态,进行不同的处理:

startupType == TPM_ST_CLEAR,则将:

1. 重置TANY_DATA,使所有会话不可用(resetSTANY_DATA (invalidates ALL sessions) );

2. 初始化会话的nonce(initsession-context nonce);

3. 重置PCR值(resetPCR values);

4. 重置STCLEAR_FLAGSSTCLEAR_DATAresetSTCLEAR_FLAGSresetSTCLEAR_DATA);

5. 为变量和PCR依赖的密钥赋特定值(flushvolatiles and PCR dependent keys);

6. 初始化密钥的nonce(initkey-context nonce);

7. 使计算器句柄不可用(invalidate counterhandle);

8. 重置NV读写标志(resetNV read and write flags)。

startupType == TPM_ST_STATE 以及 startupType == TPM_ST_DEACTIVATED时做相应的特殊处理。

3.数据文件初始化、加载、保存流程

    本部分代码主要位于tpm/tpm_data.h以及tpm/tpm_data.c中。

3.1数据初始化

    执行代码位于函数void tpm_init_data(void)中,为TPM的数据赋默认值,执行流程如下:

1. 以特定的默认值初始化背书密钥相关的n、e、p、q(基于RSA);

2. 将数据本身置为NULL、FALSE或0(reset all data to NULL, FALSE or 0);

3. 设置数据相关的各个永久标志值(set permanent flags);

4. 设置TPM版本号(set TPM vision);

5. 产生随机数(seedPRNG),执行函数:

tpm_get_extern_random_bytes(&tpmData.permanent.data.rngState,

   sizeof(tpmData.permanent.data.rngState));

6. 初始化设置24个数据存储PCR的属性(setup PCR attributes),根据不同的输入参数初始化PCR的pcrReset、pcrResetLocal、pcrExtendLocal三个属性,调用静态内联函数:

static inlinevoid init_pcr_attr(intpcr, BOOL reset, BYTE rl,BYTE el);

7. 根据参数,重新生成背书密钥(generate a new endorsement key)或设置为已有的背书密钥(setup endorsement key);

8. 根据参数,重新生成DAA种子(generate the DAA seed)或设置为已有的DAA种子(setup DAA seed);

9. 初始化预先定义好的非易失性存储设备(initialize predefinednon-volatile storage),调用函数:

static voidinit_nv_storage(void);

执行初始化非易失性存储器(nv_storage),创建默认的NV最大空间(4096字节),各个字节赋初值0xff,设置NV的各个初始属性,设置NV的(永久性)数据空间为20字节;

10. 设置超时时间与持续时间值(set the timeout andduration values),调用静态函数:

static voidinit_timeouts(void);

采用PC平台默认值,对永久性数据的tis_timeouts、cmd_durations数组进行初始化。

3.2数据加载

       执行代码位于函数int tpm_restore_permanent_data(void)中,流程如下:

1.从存储器中读出数据(readdata)到缓存变量,执行函数:

tpm_read_from_storage(&buf, &buf_length)

2.解码数据(unmarshaldata),并将数据加载到变量tpmData,先后执行函数:

tpm_unmarshal_TPM_VERSION(&ptr, &len, &ver)

tpm_unmarshal_TPM_DATA(&ptr, &len, &tpmData)

注:TPM emulator运行在内核层,可以有权利访问本地文件系统,对于永久性数据的存储采用直接访问本地文件的方式(Direct file access),在默认情况下,数据存储于本地文件(存储方法见3.3),在代码中宏定义存储文件路径与文件名为TPM_STORAGE_NAME,在emulator软件包的CMakeLists.txt中,对TPM_STORAGE_NAME进行了详细的分系统的初始化设定:对于WIN32系统,设定文件路径与文件名为"${PROGRAMFILES}/tpm_emulator-1_2_${${PROJECT_NAME}_VERSION_MAJOR}_${${PROJECT_NAME}_VERSION_MINOR}",对于利用最新的tpm_emulator 0.7.6来说,上述语句即为"${PROGRAMFILES}/tpm_emulator-1_2_0_7",${PROGRAMFILES}指程序运行所在的文件目录;对于APPLE系统,设定文件路径与文件名为"/private/var/lib/tpm/tpm_emulator-1_2_${${PROJECT_NAME}_VERSION_MAJOR}_${${PROJECT_NAME}_VERSION_MINOR}";对于其他系统,设定文件路径与文件名为"/var/lib/tpm/tpm_emulator-1_2_${${PROJECT_NAME}_VERSION_MAJOR}_${${PROJECT_NAME}_VERSION_MINOR}"。内部数据文件即存储在上述路径下,相应数据在加载时也会在对应的目录下寻找存储的数据文件。

3.3数据存储

    执行代码位于函数int tpm_store_permanent_data(void)中,流程如下:

1.按照特定排列编码数据(marshaldata),首先计算待存储数据的长度,然后调用下列函数编码:

tpm_marshal_TPM_DATA(&ptr, &len, &tpmData)

2.将编码好的数据写入存储设备,调用函数:

tpm_write_to_storage(buf,buf_length - len)

4.命令执行

    一条TPM的命令执行通过调用函数tpm_handle_command()来初始化,函数的输入参数是依规定编排好的TPM命令。随后,命令的处理分三个步骤。第一步,命令被解码成三个主要部分(见图2):请求头部(therequest header)、命令参数(the command parameters)和授权尾部(theauthorization trailer)(可选),在验证命令是否在当前的TPM状态下被允许执行后,输入参数的摘要被计算并且参数根据特定的TPM命令被进一步解编。第二步,进行实际的命令执行,并且设置命令响应参数,如果需要,授权尾部将被验证,以便保证命令的授权和输入参数的完整性。第三步,响应授权被计算,其结果与输出参数和响应头部相组合。最后,响应被编排成字节表示并被返回给调用者。

TPM emulator 调研报告_第1张图片

图2:TPM命令结构

    本部分代码主要位于tpm/tpm_cmd_handler.c中,具体步骤如下:

1.解码命令请求包,并验证包的完整性(weneed the whole packet at once, otherwise unmarshalling will fail),调用函数tpm_unmarshal_TPM_REQUEST

2.更新计时器的计数(updatetiming ticks),调用函数tpm_update_ticks

3.审计请求(auditreques),调用函数tpm_audit_request

4.执行具体的命令(executecommand),调用函数tpm_execute_command,函数具体流程如下:

    4.1设置授权、响应标签及响应长度(setupauthorisation as well as response tag and size),如果标签值正常,则进入下一步;

    4.2检查在TPM当前的模式下命令是否是被允许执行的(checkwhether the command is allowed in the current mode of the TPM),调用函数tpm_check_status_and_mode,如果允许,则进入下一步;

    4.3根据命令的序数标志,处理相对应的命令操作(handlecommand ordinal),并将执行结果赋予响应变量,如果命令序数标志正确,则进入下一步;

    4.4设置响应(setupresponse),如果之前响应成功获得,则进行相应属性设置,进入下一步;

    4.5如果需要,终结授权的会话(terminateauthorization sessions if necessary);

    4.6如果传输互斥(独占性)标志被置位,则设定只有TPM执行传输和传输签名释放被允许执行(iftransportExclusive is set, only the execution of TPM_ExecuteTransport andTPM_ReleaseTransportSigned is allowed)。

5.审计响应(auditresponse),调用函数tpm_audit_response

6.初始化输出,编码响应结果(initoutput and marshal response),调用函数tpm_marshal_TPM_RESPONSE

5.TPM emulator 0.7.4与0.6的区别

    TPM emulator 0.6于2009年9月发布,目前我组已成功移植到ARM板上;最新的版本TPM emulator 0.7.4于2011年12月发布,较0.6版本有以下区别:

1.0.7.4增加了移动可信模块(MTM),此模块与基本的TPM在功能和命令上都有一定差异;

2.0.6采用的是Makefile的自动化编译方式,代码包内是Makefile文件,更加侧重方便与UNIX下的编译,而0.7.4采用的是CMake方式的集成自动化编译,代码包内是CMakeLists.txt文件,需要安装编译工具,但其跨平台性更好,具有更丰富的功能;

3.0.74的设备驱动库(tddl)代码文件夹中,专为UNIX与Windows系统编写了头文件,分别为tddl_unix.h和tddl_windows.h,使驱动与特定系统的兼容性更好,这两个文件在0.6中并没有;

4.0.6中对于内部数据存储的目录设定是通过代码包中tpmd文件夹下tpm_emulator_config.h与tpmd.c文件完成设定的,而0.7.4在tpm文件夹中另写了两个全局变量文件tpm_emulator_extern.h和tpm_emulator_extern.c,这里面定义了数据存储的目录,但没有具体赋值,最后的目录设定是在最终的CMakeLists.txt中完成的,此外,上述两个全局变量文件定义了相关的全局变量供其他代码文件使用,对WIN32和WIN64系统做出了特别的支持;

5.在用户空间守护程序(tpmd)的代码文件夹中0.6仍是对所有操作系统编写的统一代码,而0.7.4对UNIX和Windows分别进行了代码编写;

6.对于内核模块(tpmd_dev)的代码编写,0.6主要针对Linux及OpenBSD进行编写,而0.7.4还对Darwin进行了额外的编写。

你可能感兴趣的:(emulator,调研,tpm,tpm,可信计算)