跳过正文
  1. 文章/

简单了解和手动使用 TPM

·1918 字·4 分钟
目录

最近疯狂迷恋 tpm,这个小玩意究竟是怎么实现基于设备的安全的?

tpm 的相关属性
#

第一步,我们先看下 tpm 有什么变量是可以被改变的:

sudo tpm2_getcap properties-variable

第一组:永久属性组 (PT -> Property)
#

TPM2_PT_PERMANENT:
  ownerAuthSet:              0 # 所有者鉴权密码
  endorsementAuthSet:        0 # 背书鉴权密码
  lockoutAuthSet:            0 # 锁定鉴权密码
  reserved1:                 0
  disableClear:              0
  inLockout:                 0
  tpmGeneratedEPS:           1
  reserved2:                 0

第二组:启动时状态
#

TPM2_PT_STARTUP_CLEAR:
  phEnable:                  1 # 平台层级,也就是你可以在系统里面清除tpm所有信息
  shEnable:                  1 # 存储层级,其实跟owner是一样的
  ehEnable:                  1 # 背书层级
  phEnableNV:                1
  reserved1:                 0
  orderly:                   1 

第三组:句柄资源 (HR -> Handle Resource)
#

TPM2_PT_HR_NV_INDEX: 0x5
TPM2_PT_HR_LOADED: 0x0 # 当前已加载密钥
TPM2_PT_HR_LOADED_AVAIL: 0x3 
TPM2_PT_HR_ACTIVE: 0x0 # 已激活的会话
TPM2_PT_HR_ACTIVE_AVAIL: 0x40 
TPM2_PT_HR_TRANSIENT_AVAIL: 0x3 # 临时会话,从存储读入内存
TPM2_PT_HR_PERSISTENT: 0x0 # 可以永久存储的槽位
TPM2_PT_HR_PERSISTENT_AVAIL: 0x2

第四组:锁定相关配置
#

TPM2_PT_LOCKOUT_COUNTER: 0x0
TPM2_PT_MAX_AUTH_FAIL: 0xC8
TPM2_PT_LOCKOUT_INTERVAL: 0x0
TPM2_PT_LOCKOUT_RECOVERY: 0x0

第五组:杂项
#

TPM2_PT_NV_COUNTERS: 0x0 # 非易失标志位
TPM2_PT_NV_COUNTERS_AVAIL: 0x8
TPM2_PT_ALGORITHM_SET: 0xFFFFFFFF
TPM2_PT_LOADED_CURVES: 0x3
TPM2_PT_AUDIT_COUNTER_0: 0x0 # 审计日志
TPM2_PT_AUDIT_COUNTER_1: 0x0

不同层级
#

Owner Hierarchy: 你平时用来创建密钥、存储数据的权限。

Endorsement Hierarchy: 涉及设备隐私和证书的权限。

Lockout Hierarchy: “锁定控制”权限,用于控制锁定策略:

  • 允许多少次普通密码尝试后进入锁定模式 (TPM2_PT_MAX_AUTH_FAIL)
  • 被记录下连续失败后,过多久次数才能减一 (TPM2_PT_LOCKOUT_INTERVAL)
  • 进入锁定模式后,过多久才能解锁 (TPM2_PT_LOCKOUT_RECOVERY)

⚠️注意:如果你是在验证以上三种层级的管理密码,比如用 tpm2_clear -C o -c -p 的时候,只有一次机会,失败立即进入锁定模式。

你将被限制到 TPM2_PT_LOCKOUT_RECOVERY 的时间秒数之后才能解锁。 这两个时间设成 0,永远解不了,只能清除。

Platform Hierarchy: 唯一不受 DA 限制的密码尝试。但是就算你拿到了权限,除了能够清除所有的密钥和密码,没有任何别的权限。


初始化自己的 tpm 配置
#

清除tpm2_clear

有的平台可以直接用tpm2_clear -c p清掉,有的不行只能到 bios 里面清。

修改锁定设置tpm2_dictionarylockout (也叫 DA,dictionary attack)

# 分别对应上面的“锁定层级”的三个变量,下例为windows标准设置
sudo tpm2_dictionarylockout -s -n 32 -t 7200 -l 86400

修改密码tpm2_changeauth

sudo tpm2_changeauth -c lockout <new_password>

这里有个彩蛋,windows 一开始看到新的 tpm,跟我们一样改完了,直接把钥匙扔了,惊喜吧?

所以你只能重置,然后自己设个密码,好在 windows 不会改你的密码回去。


开始使用
#

创建主密钥,得到一个 tpm 的内存快照,这个主密钥,相比于密钥的说法,其实更是一个 token,因为是由 TPM 这一个生命周期的种子派生的,所以每次用同样的输入得到同样的输出,这里不是指的 ctx 的 hash 相同,而是每次读取的公钥相同。

如果还原了 tpm,签名会变,ctx 永远不再可用。

# 创一个新的
❯ tpm2_createprimary -C o -c primary2.ctx
name-alg:
  value: sha256
  raw: 0xb
attributes:
  value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt
  raw: 0x30072
type:
  value: rsa
  raw: 0x1
exponent: 65537
bits: 2048
scheme:
  value: null
  raw: 0x10
scheme-halg:
  value: (null)
  raw: 0x0
sym-alg:
  value: aes
  raw: 0x6
sym-mode:
  value: cfb
  raw: 0x43
sym-keybits: 128
rsa: c26b4cbadd50e487645f22d72f378953282d9a95036f09815fd91b2fd360f85517e730b19c1ba5668452b4ceca94a38aa6883ba6204f1ea4f72bd644190186fd1806f486e0e9de1ebe4fd64f618a0aac2d120e766bd0575f19b0ecce6ff55df47bacdffb8a431ac8d75cadff88ec62f149ae99ea210da4ba315b2d849b1e2b9196f7baac40ee34382f063e69af448b0c5812319b1efaa7cdffe50d3e4c396e427b270c95676b48168b2b90de555dd1cb46cbdeaed6c4f5c8466fa6a977af67a70efdf673a05b3fe7577bfb7d55991a0b7e94e917fc2533975c13c26231b3d2f6e990c395f963728f3dcaecd8f656f4a5a709fbd5c7edbda773ce960d0c9076c3

# 读一个之前创的
❯ tpm2_readpublic -c primary.ctx
name: 000bf2c2daef8da6f674f7cdd47edb780e14baec9ed14a212904d79ca92c93e05c7c
qualified name: 000b8a569c59019bf73a23befe7aeb9e54c74470737c0288f63d2481324388e1edfb
name-alg:
  value: sha256
  raw: 0xb
attributes:
  value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt
  raw: 0x30072
type:
  value: rsa
  raw: 0x1
exponent: 65537
bits: 2048
scheme:
  value: null
  raw: 0x10
scheme-halg:
  value: (null)
  raw: 0x0
sym-alg:
  value: aes
  raw: 0x6
sym-mode:
  value: cfb
  raw: 0x43
sym-keybits: 128
rsa: c26b4cbadd50e487645f22d72f378953282d9a95036f09815fd91b2fd360f85517e730b19c1ba5668452b4ceca94a38aa6883ba6204f1ea4f72bd644190186fd1806f486e0e9de1ebe4fd64f618a0aac2d120e766bd0575f19b0ecce6ff55df47bacdffb8a431ac8d75cadff88ec62f149ae99ea210da4ba315b2d849b1e2b9196f7baac40ee34382f063e69af448b0c5812319b1efaa7cdffe50d3e4c396e427b270c95676b48168b2b90de555dd1cb46cbdeaed6c4f5c8466fa6a977af67a70efdf673a05b3fe7577bfb7d55991a0b7e94e917fc2533975c13c26231b3d2f6e990c395f963728f3dcaecd8f656f4a5a709fbd5c7edbda773ce960d0c9076c3

加密文字
#

在主密钥下面创建一个子密钥:

echo -n "hello" | tpm2_create -C primary.ctx \
  -u key.pub -r key.priv \
  -i- \
  -p "123456"

生成一个 tpm 的内存快照:

tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx

把它持久化到 tpm 的对象里面:

sudo tpm2_evictcontrol -C o -c key.ctx 0x81010002

有点乱?总结一下

命令说明: tpm2_create 指的是创建一个"密封信件",这个信件有公钥来标识身份,有加密 blob 来给 tpm 读。 tpm2_load 则是让 tpm 读取这个密封信件,他生成一个内存快照 (token),这个东西可以持久化来被 tpm 引用(成为永久句柄)。 tpm2_evictcontrol 如果-c 加的是文件,然后再跟上地址,语义就是把这个文件持久化到 tpm 的一个句柄中;如果-c 加的是一个地址,那么则是把这个持久句柄驱逐。

对这里出现的文件进行说明:

  • key.pub 可以开放给别人看
  • key.priv 是被 tpm 加密的对象,只有 tpm 才能解密
  • key.ctx 是 tpm 的内存快照,和 0x 开头的是同一个类型的东西,都可以被-c加载

所以我们给他解个密:

❯ tpm2_unseal -c key.ctx -p 123456
hello%
❯ tpm2_unseal -c 0x81010002 -p 123456
hello%  

验证签名
#

既然懂了一些,我们就不要用 rsa 了,用 ecc,只需把所有的创建密钥的过程加-G ecc就行。

如果要删除之前的,善用 evictcontrol。

重新创建 ecc 的主密钥:

tpm2_createprimary -C o -G ecc -c ecc_primary.ctx

创建一个签名的对象:

tpm2_create \
  -C ecc_primary.ctx \
  -G ecc256:ecdsa \
  -u ecc.pub \
  -r ecc.priv \
  -p 123456

加载:

tpm2_load \
  -C ecc_primary.ctx \
  -u ecc.pub \
  -r ecc.priv \
  -c ecc.ctx

持久化:

sudo tpm2_evictcontrol -C o -c ecc.ctx 0x81020000

生成一个固定的挑战,用于验证(其实真正的过程中,每次都是不同的):

head -c 32 /dev/urandom > challenge.bin

用 tpm 自带的工具验证:

❯ tpm2_sign \
  -c 0x81020000 \
  -g sha256 \
  -s ecdsa \
  -o sig.tss \
  -p 123456 \
  challenge.bin
❯ tpm2_verifysignature \
  -c 0x81020000 \
  -g sha256 \
  -s sig.tss \
  -m challenge.bin
# 返回0,通过

整活
#

接下来把这个接入 pam。

导出公钥:

tpm2_readpublic -c 0x81020000 -f pem -o /etc/tpm-ecc.pub.pem
chmod 644 /etc/tpm-ecc.pub.pem

用我这个项目试试,仅供娱乐:

#%PAM-1.0

auth       required                    pam_faillock.so      preauth
auth       [success=3 default=ignore]  pam_tpm_ecc.so       key_handle=0x81020000 pubkey=/etc/tpm-ecc.pub.pem
-auth      [success=2 default=ignore]  pam_systemd_home.so
auth       [success=1 default=bad]     pam_unix.so          try_first_pass nullok
auth       [default=die]               pam_faillock.so      authfail
auth       optional                    pam_permit.so
auth       required                    pam_env.so
auth       required                    pam_faillock.so      authsucc

polkit
#

这玩意,搞那么复杂的权限管理,还用模板,没折腾死我。

他每次授权的时候启动一个 helper,这个 helper 有 root 权限,但是启动的时候被 systemd 限制了一大堆特权,我们得用 drop-in 给他加回来:

sudo systemctl edit polkit-agent-helper@
# Editing /etc/systemd/system/polkit-agent-helper@.service.d/override.conf
[Service]                        
DeviceAllow=/dev/tpmrm0 rw
BindPaths=/dev/tpmrm0

彩蛋
#

一发入魄

❯ sudo tpm2_dictionarylockout -s -n 10 -t 600 -l 60
WARNING:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:310:Esys_DictionaryAttackParameters_Finish() Received TPM Error 
ERROR:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:108:Esys_DictionaryAttackParameters() Esys Finish ErrorCode (0x0000098e) 
ERROR: Esys_DictionaryAttackParameters(0x98E) - tpm:session(1):the authorization HMAC check failed and DA counter incremented
ERROR: Failed DictionaryLockout Setup
ERROR: Unable to run tpm2_dictionarylockout
❯ sudo tpm2_dictionarylockout -s -n 10 -t 600 -l 60
WARNING:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:310:Esys_DictionaryAttackParameters_Finish() Received TPM Error 
ERROR:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:108:Esys_DictionaryAttackParameters() Esys Finish ErrorCode (0x00000921) 
ERROR: Esys_DictionaryAttackParameters(0x921) - tpm:warn(2.0): authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA lockout mode
ERROR: Failed DictionaryLockout Setup
ERROR: Unable to run tpm2_dictionarylockout

相关文章