Skip to content

Commit 88ab370

Browse files
committed
new(tpm)
1 parent 781ae65 commit 88ab370

1 file changed

Lines changed: 358 additions & 0 deletions

File tree

content/posts/tpm/index.md

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
+++
2+
date = '2026-05-05T16:49:51+08:00'
3+
draft = false
4+
title = '简单了解和手动使用 TPM'
5+
tags = ['Linux', 'Arch Linux']
6+
+++
7+
8+
9+
最近疯狂迷恋 tpm,这个小玩意究竟是怎么实现基于设备的安全的?
10+
11+
## tpm 的相关属性
12+
13+
第一步,我们先看下 tpm 有什么变量是可以被改变的:
14+
15+
```bash
16+
sudo tpm2_getcap properties-variable
17+
```
18+
19+
### 第一组:永久属性组 (PT -> Property)
20+
21+
```bash
22+
TPM2_PT_PERMANENT:
23+
ownerAuthSet: 0 # 所有者鉴权密码
24+
endorsementAuthSet: 0 # 背书鉴权密码
25+
lockoutAuthSet: 0 # 锁定鉴权密码
26+
reserved1: 0
27+
disableClear: 0
28+
inLockout: 0
29+
tpmGeneratedEPS: 1
30+
reserved2: 0
31+
```
32+
33+
### 第二组:启动时状态
34+
35+
```bash
36+
TPM2_PT_STARTUP_CLEAR:
37+
phEnable: 1 # 平台层级,也就是你可以在系统里面清除tpm所有信息
38+
shEnable: 1 # 存储层级,其实跟owner是一样的
39+
ehEnable: 1 # 背书层级
40+
phEnableNV: 1
41+
reserved1: 0
42+
orderly: 1
43+
```
44+
45+
### 第三组:句柄资源 (HR -> Handle Resource)
46+
47+
```bash
48+
TPM2_PT_HR_NV_INDEX: 0x5
49+
TPM2_PT_HR_LOADED: 0x0 # 当前已加载密钥
50+
TPM2_PT_HR_LOADED_AVAIL: 0x3
51+
TPM2_PT_HR_ACTIVE: 0x0 # 已激活的会话
52+
TPM2_PT_HR_ACTIVE_AVAIL: 0x40
53+
TPM2_PT_HR_TRANSIENT_AVAIL: 0x3 # 临时会话,从存储读入内存
54+
TPM2_PT_HR_PERSISTENT: 0x0 # 可以永久存储的槽位
55+
TPM2_PT_HR_PERSISTENT_AVAIL: 0x2
56+
```
57+
58+
### 第四组:锁定相关配置
59+
60+
```bash
61+
TPM2_PT_LOCKOUT_COUNTER: 0x0
62+
TPM2_PT_MAX_AUTH_FAIL: 0xC8
63+
TPM2_PT_LOCKOUT_INTERVAL: 0x0
64+
TPM2_PT_LOCKOUT_RECOVERY: 0x0
65+
```
66+
67+
### 第五组:杂项
68+
69+
```bash
70+
TPM2_PT_NV_COUNTERS: 0x0 # 非易失标志位
71+
TPM2_PT_NV_COUNTERS_AVAIL: 0x8
72+
TPM2_PT_ALGORITHM_SET: 0xFFFFFFFF
73+
TPM2_PT_LOADED_CURVES: 0x3
74+
TPM2_PT_AUDIT_COUNTER_0: 0x0 # 审计日志
75+
TPM2_PT_AUDIT_COUNTER_1: 0x0
76+
```
77+
78+
---
79+
80+
### 不同层级
81+
82+
83+
**Owner Hierarchy**: 你平时用来创建密钥、存储数据的权限。
84+
85+
**Endorsement Hierarchy**: 涉及设备隐私和证书的权限。
86+
87+
**Lockout Hierarchy**: “锁定控制”权限,用于控制锁定策略:
88+
89+
- 允许多少次普通密码尝试后进入锁定模式 (TPM2_PT_MAX_AUTH_FAIL)
90+
- 被记录下连续失败后,过多久次数才能减一 (TPM2_PT_LOCKOUT_INTERVAL)
91+
- 进入锁定模式后,过多久才能解锁 (TPM2_PT_LOCKOUT_RECOVERY)
92+
93+
> ⚠️注意:如果你是在验证以上三种层级的管理密码,比如用 tpm2_clear -C o -c -p <password>的时候,只有一次机会,失败立即进入锁定模式。
94+
>
95+
> 你将被限制到 TPM2_PT_LOCKOUT_RECOVERY 的时间秒数之后才能解锁。
96+
> 这两个时间设成 0,永远解不了,只能清除。
97+
98+
**Platform Hierarchy**: 唯一不受 DA 限制的密码尝试。但是就算你拿到了权限,除了能够清除所有的密钥和密码,没有任何别的权限。
99+
100+
---
101+
102+
### 初始化自己的 tpm 配置
103+
104+
**清除**`tpm2_clear`
105+
106+
有的平台可以直接用`tpm2_clear -c p`清掉,有的不行只能到 bios 里面清。
107+
108+
**修改锁定设置**`tpm2_dictionarylockout` (也叫 DA,dictionary attack)
109+
110+
```bash
111+
# 分别对应上面的“锁定层级”的三个变量,下例为windows标准设置
112+
sudo tpm2_dictionarylockout -s -n 32 -t 7200 -l 86400
113+
```
114+
115+
**修改密码**`tpm2_changeauth`
116+
117+
```bash
118+
sudo tpm2_changeauth -c lockout <new_password>
119+
```
120+
121+
这里有个彩蛋,windows 一开始看到新的 tpm,跟我们一样改完了,直接把钥匙扔了,惊喜吧?
122+
123+
所以你只能重置,然后自己设个密码,好在 windows 不会改你的密码回去。
124+
125+
---
126+
## 开始使用
127+
128+
创建主密钥,得到一个 tpm 的内存快照,这个主密钥,相比于密钥的说法,其实更是一个 token,因为是由 TPM 这一个生命周期的种子派生的,所以每次用同样的输入得到同样的输出,这里不是指的 ctx 的 hash 相同,而是每次读取的公钥相同。
129+
130+
如果还原了 tpm,签名会变,ctx 永远不再可用。
131+
132+
```bash
133+
# 创一个新的
134+
❯ tpm2_createprimary -C o -c primary2.ctx
135+
name-alg:
136+
value: sha256
137+
raw: 0xb
138+
attributes:
139+
value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt
140+
raw: 0x30072
141+
type:
142+
value: rsa
143+
raw: 0x1
144+
exponent: 65537
145+
bits: 2048
146+
scheme:
147+
value: null
148+
raw: 0x10
149+
scheme-halg:
150+
value: (null)
151+
raw: 0x0
152+
sym-alg:
153+
value: aes
154+
raw: 0x6
155+
sym-mode:
156+
value: cfb
157+
raw: 0x43
158+
sym-keybits: 128
159+
rsa: c26b4cbadd50e487645f22d72f378953282d9a95036f09815fd91b2fd360f85517e730b19c1ba5668452b4ceca94a38aa6883ba6204f1ea4f72bd644190186fd1806f486e0e9de1ebe4fd64f618a0aac2d120e766bd0575f19b0ecce6ff55df47bacdffb8a431ac8d75cadff88ec62f149ae99ea210da4ba315b2d849b1e2b9196f7baac40ee34382f063e69af448b0c5812319b1efaa7cdffe50d3e4c396e427b270c95676b48168b2b90de555dd1cb46cbdeaed6c4f5c8466fa6a977af67a70efdf673a05b3fe7577bfb7d55991a0b7e94e917fc2533975c13c26231b3d2f6e990c395f963728f3dcaecd8f656f4a5a709fbd5c7edbda773ce960d0c9076c3
160+
161+
# 读一个之前创的
162+
❯ tpm2_readpublic -c primary.ctx
163+
name: 000bf2c2daef8da6f674f7cdd47edb780e14baec9ed14a212904d79ca92c93e05c7c
164+
qualified name: 000b8a569c59019bf73a23befe7aeb9e54c74470737c0288f63d2481324388e1edfb
165+
name-alg:
166+
value: sha256
167+
raw: 0xb
168+
attributes:
169+
value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt
170+
raw: 0x30072
171+
type:
172+
value: rsa
173+
raw: 0x1
174+
exponent: 65537
175+
bits: 2048
176+
scheme:
177+
value: null
178+
raw: 0x10
179+
scheme-halg:
180+
value: (null)
181+
raw: 0x0
182+
sym-alg:
183+
value: aes
184+
raw: 0x6
185+
sym-mode:
186+
value: cfb
187+
raw: 0x43
188+
sym-keybits: 128
189+
rsa: c26b4cbadd50e487645f22d72f378953282d9a95036f09815fd91b2fd360f85517e730b19c1ba5668452b4ceca94a38aa6883ba6204f1ea4f72bd644190186fd1806f486e0e9de1ebe4fd64f618a0aac2d120e766bd0575f19b0ecce6ff55df47bacdffb8a431ac8d75cadff88ec62f149ae99ea210da4ba315b2d849b1e2b9196f7baac40ee34382f063e69af448b0c5812319b1efaa7cdffe50d3e4c396e427b270c95676b48168b2b90de555dd1cb46cbdeaed6c4f5c8466fa6a977af67a70efdf673a05b3fe7577bfb7d55991a0b7e94e917fc2533975c13c26231b3d2f6e990c395f963728f3dcaecd8f656f4a5a709fbd5c7edbda773ce960d0c9076c3
190+
191+
```
192+
193+
### 加密文字
194+
195+
在主密钥下面创建一个子密钥:
196+
197+
```bash
198+
echo -n "hello" | tpm2_create -C primary.ctx \
199+
-u key.pub -r key.priv \
200+
-i- \
201+
-p "123456"
202+
```
203+
204+
生成一个 tpm 的内存快照:
205+
206+
```bash
207+
tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx
208+
```
209+
210+
把它持久化到 tpm 的对象里面:
211+
212+
```bash
213+
sudo tpm2_evictcontrol -C o -c key.ctx 0x81010002
214+
```
215+
216+
---
217+
> 有点乱?总结一下
218+
219+
**命令**说明:
220+
tpm2_create 指的是创建一个"密封信件",这个信件有公钥来标识身份,有加密 blob 来给 tpm 读。
221+
tpm2_load 则是让 tpm 读取这个密封信件,他生成一个内存快照 (token),这个东西可以持久化来被 tpm 引用(成为永久句柄)。
222+
tpm2_evictcontrol 如果-c 加的是文件,然后再跟上地址,语义就是把这个文件持久化到 tpm 的一个句柄中;如果-c 加的是一个地址,那么则是把这个持久句柄驱逐。
223+
224+
对这里出现的**文件**进行说明:
225+
226+
- key.pub 可以开放给别人看
227+
- key.priv 是被 tpm 加密的对象,只有 tpm 才能解密
228+
- key.ctx 是 tpm 的内存快照,和 0x 开头的是同一个类型的东西,都可以被`-c`加载
229+
230+
---
231+
232+
所以我们给他解个密:
233+
234+
```bash
235+
❯ tpm2_unseal -c key.ctx -p 123456
236+
hello%
237+
❯ tpm2_unseal -c 0x81010002 -p 123456
238+
hello%
239+
```
240+
241+
### 验证签名
242+
既然懂了一些,我们就不要用 rsa 了,用 ecc,只需把所有的创建密钥的过程加`-G ecc`就行。
243+
244+
如果要删除之前的,善用 evictcontrol。
245+
246+
重新创建 ecc 的主密钥:
247+
```bash
248+
tpm2_createprimary -C o -G ecc -c ecc_primary.ctx
249+
```
250+
251+
创建一个签名的对象:
252+
253+
```bash
254+
tpm2_create \
255+
-C ecc_primary.ctx \
256+
-G ecc256:ecdsa \
257+
-u ecc.pub \
258+
-r ecc.priv \
259+
-p 123456
260+
```
261+
262+
加载:
263+
264+
```bash
265+
tpm2_load \
266+
-C ecc_primary.ctx \
267+
-u ecc.pub \
268+
-r ecc.priv \
269+
-c ecc.ctx
270+
```
271+
272+
持久化:
273+
```bash
274+
sudo tpm2_evictcontrol -C o -c ecc.ctx 0x81020000
275+
```
276+
277+
生成一个固定的挑战,用于验证(其实真正的过程中,每次都是不同的):
278+
279+
```bash
280+
head -c 32 /dev/urandom > challenge.bin
281+
```
282+
283+
用 tpm 自带的工具验证:
284+
285+
```bash
286+
❯ tpm2_sign \
287+
-c 0x81020000 \
288+
-g sha256 \
289+
-s ecdsa \
290+
-o sig.tss \
291+
-p 123456 \
292+
challenge.bin
293+
❯ tpm2_verifysignature \
294+
-c 0x81020000 \
295+
-g sha256 \
296+
-s sig.tss \
297+
-m challenge.bin
298+
# 返回0,通过
299+
```
300+
301+
---
302+
303+
## 整活
304+
接下来把这个接入 pam。
305+
306+
导出公钥:
307+
```bash
308+
tpm2_readpublic -c 0x81020000 -f pem -o /etc/tpm-ecc.pub.pem
309+
chmod 644 /etc/tpm-ecc.pub.pem
310+
```
311+
312+
用我这个[项目](https://github.com/minortex/tpm_auth)试试,仅供娱乐:
313+
```bash
314+
#%PAM-1.0
315+
316+
auth required pam_faillock.so preauth
317+
auth [success=3 default=ignore] pam_tpm_ecc.so key_handle=0x81020000 pubkey=/etc/tpm-ecc.pub.pem
318+
-auth [success=2 default=ignore] pam_systemd_home.so
319+
auth [success=1 default=bad] pam_unix.so try_first_pass nullok
320+
auth [default=die] pam_faillock.so authfail
321+
auth optional pam_permit.so
322+
auth required pam_env.so
323+
auth required pam_faillock.so authsucc
324+
```
325+
326+
### polkit
327+
328+
这玩意,搞那么复杂的权限管理,还用模板,没折腾死我。
329+
330+
他每次授权的时候启动一个 helper,这个 helper 有 root 权限,但是启动的时候被 systemd 限制了一大堆特权,我们得用 drop-in 给他加回来:
331+
332+
```bash
333+
sudo systemctl edit polkit-agent-helper@
334+
```
335+
336+
```ini
337+
# Editing /etc/systemd/system/polkit-agent-helper@.service.d/override.conf
338+
[Service]
339+
DeviceAllow=/dev/tpmrm0 rw
340+
BindPaths=/dev/tpmrm0
341+
```
342+
343+
## 彩蛋
344+
**一发入魄**
345+
```bash
346+
❯ sudo tpm2_dictionarylockout -s -n 10 -t 600 -l 60
347+
WARNING:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:310:Esys_DictionaryAttackParameters_Finish() Received TPM Error
348+
ERROR:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:108:Esys_DictionaryAttackParameters() Esys Finish ErrorCode (0x0000098e)
349+
ERROR: Esys_DictionaryAttackParameters(0x98E) - tpm:session(1):the authorization HMAC check failed and DA counter incremented
350+
ERROR: Failed DictionaryLockout Setup
351+
ERROR: Unable to run tpm2_dictionarylockout
352+
❯ sudo tpm2_dictionarylockout -s -n 10 -t 600 -l 60
353+
WARNING:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:310:Esys_DictionaryAttackParameters_Finish() Received TPM Error
354+
ERROR:esys:src/tss2-esys/api/Esys_DictionaryAttackParameters.c:108:Esys_DictionaryAttackParameters() Esys Finish ErrorCode (0x00000921)
355+
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
356+
ERROR: Failed DictionaryLockout Setup
357+
ERROR: Unable to run tpm2_dictionarylockout
358+
```

0 commit comments

Comments
 (0)