diff options
author | Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> | 2019-02-04 08:16:40 -0500 |
---|---|---|
committer | Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> | 2019-02-08 16:13:02 -0500 |
commit | 6ea3dfe1e0732c5bd3be1e073690b06a83c03c25 (patch) | |
tree | 12eba15c9ac44d0a84f94e653891c3dfbe35c3c4 /tools | |
parent | 8ab547a2dcfac6ec184a5e036e1093eb3f7a215c (diff) |
selftests: add TPM 2.0 tests
Added the tests that I've been using for testing TPM 2.0 functionality
for a long time but have been out-of-tree so far, residing in
https://github.com/jsakkine-intel/tpm2-scripts
Cc: Tadeusz Struk <tadeusz.struk@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Acked-by: Joey Pabalinas <joeypabalinas@gmail.com>
Reviewed-by: Petr Vorel <petr.vorel@gmail.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/Makefile | 1 | ||||
-rw-r--r-- | tools/testing/selftests/tpm2/Makefile | 4 | ||||
-rwxr-xr-x | tools/testing/selftests/tpm2/test_smoke.sh | 4 | ||||
-rwxr-xr-x | tools/testing/selftests/tpm2/test_space.sh | 4 | ||||
-rw-r--r-- | tools/testing/selftests/tpm2/tpm2.py | 696 | ||||
-rw-r--r-- | tools/testing/selftests/tpm2/tpm2_tests.py | 227 |
6 files changed, 936 insertions, 0 deletions
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 1a2bd15c5b6e..a74ce2feea29 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
@@ -47,6 +47,7 @@ TARGETS += sysctl | |||
47 | ifneq (1, $(quicktest)) | 47 | ifneq (1, $(quicktest)) |
48 | TARGETS += timers | 48 | TARGETS += timers |
49 | endif | 49 | endif |
50 | TARGETS += tpm2 | ||
50 | TARGETS += user | 51 | TARGETS += user |
51 | TARGETS += vm | 52 | TARGETS += vm |
52 | TARGETS += x86 | 53 | TARGETS += x86 |
diff --git a/tools/testing/selftests/tpm2/Makefile b/tools/testing/selftests/tpm2/Makefile new file mode 100644 index 000000000000..9dd848427a7b --- /dev/null +++ b/tools/testing/selftests/tpm2/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | ||
2 | include ../lib.mk | ||
3 | |||
4 | TEST_PROGS := test_smoke.sh test_space.sh | ||
diff --git a/tools/testing/selftests/tpm2/test_smoke.sh b/tools/testing/selftests/tpm2/test_smoke.sh new file mode 100755 index 000000000000..80521d46220c --- /dev/null +++ b/tools/testing/selftests/tpm2/test_smoke.sh | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/bash | ||
2 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | ||
3 | |||
4 | python -m unittest -v tpm2_tests.SmokeTest | ||
diff --git a/tools/testing/selftests/tpm2/test_space.sh b/tools/testing/selftests/tpm2/test_space.sh new file mode 100755 index 000000000000..a6f5e346635e --- /dev/null +++ b/tools/testing/selftests/tpm2/test_space.sh | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/bash | ||
2 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | ||
3 | |||
4 | python -m unittest -v tpm2_tests.SpaceTest | ||
diff --git a/tools/testing/selftests/tpm2/tpm2.py b/tools/testing/selftests/tpm2/tpm2.py new file mode 100644 index 000000000000..40ea95ce2ead --- /dev/null +++ b/tools/testing/selftests/tpm2/tpm2.py | |||
@@ -0,0 +1,696 @@ | |||
1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | ||
2 | |||
3 | import hashlib | ||
4 | import os | ||
5 | import socket | ||
6 | import struct | ||
7 | import sys | ||
8 | import unittest | ||
9 | from fcntl import ioctl | ||
10 | |||
11 | |||
12 | TPM2_ST_NO_SESSIONS = 0x8001 | ||
13 | TPM2_ST_SESSIONS = 0x8002 | ||
14 | |||
15 | TPM2_CC_FIRST = 0x01FF | ||
16 | |||
17 | TPM2_CC_CREATE_PRIMARY = 0x0131 | ||
18 | TPM2_CC_DICTIONARY_ATTACK_LOCK_RESET = 0x0139 | ||
19 | TPM2_CC_CREATE = 0x0153 | ||
20 | TPM2_CC_LOAD = 0x0157 | ||
21 | TPM2_CC_UNSEAL = 0x015E | ||
22 | TPM2_CC_FLUSH_CONTEXT = 0x0165 | ||
23 | TPM2_CC_START_AUTH_SESSION = 0x0176 | ||
24 | TPM2_CC_GET_CAPABILITY = 0x017A | ||
25 | TPM2_CC_PCR_READ = 0x017E | ||
26 | TPM2_CC_POLICY_PCR = 0x017F | ||
27 | TPM2_CC_PCR_EXTEND = 0x0182 | ||
28 | TPM2_CC_POLICY_PASSWORD = 0x018C | ||
29 | TPM2_CC_POLICY_GET_DIGEST = 0x0189 | ||
30 | |||
31 | TPM2_SE_POLICY = 0x01 | ||
32 | TPM2_SE_TRIAL = 0x03 | ||
33 | |||
34 | TPM2_ALG_RSA = 0x0001 | ||
35 | TPM2_ALG_SHA1 = 0x0004 | ||
36 | TPM2_ALG_AES = 0x0006 | ||
37 | TPM2_ALG_KEYEDHASH = 0x0008 | ||
38 | TPM2_ALG_SHA256 = 0x000B | ||
39 | TPM2_ALG_NULL = 0x0010 | ||
40 | TPM2_ALG_CBC = 0x0042 | ||
41 | TPM2_ALG_CFB = 0x0043 | ||
42 | |||
43 | TPM2_RH_OWNER = 0x40000001 | ||
44 | TPM2_RH_NULL = 0x40000007 | ||
45 | TPM2_RH_LOCKOUT = 0x4000000A | ||
46 | TPM2_RS_PW = 0x40000009 | ||
47 | |||
48 | TPM2_RC_SIZE = 0x01D5 | ||
49 | TPM2_RC_AUTH_FAIL = 0x098E | ||
50 | TPM2_RC_POLICY_FAIL = 0x099D | ||
51 | TPM2_RC_COMMAND_CODE = 0x0143 | ||
52 | |||
53 | TSS2_RC_LAYER_SHIFT = 16 | ||
54 | TSS2_RESMGR_TPM_RC_LAYER = (11 << TSS2_RC_LAYER_SHIFT) | ||
55 | |||
56 | TPM2_CAP_HANDLES = 0x00000001 | ||
57 | TPM2_CAP_COMMANDS = 0x00000002 | ||
58 | TPM2_CAP_TPM_PROPERTIES = 0x00000006 | ||
59 | |||
60 | TPM2_PT_FIXED = 0x100 | ||
61 | TPM2_PT_TOTAL_COMMANDS = TPM2_PT_FIXED + 41 | ||
62 | |||
63 | HR_SHIFT = 24 | ||
64 | HR_LOADED_SESSION = 0x02000000 | ||
65 | HR_TRANSIENT = 0x80000000 | ||
66 | |||
67 | SHA1_DIGEST_SIZE = 20 | ||
68 | SHA256_DIGEST_SIZE = 32 | ||
69 | |||
70 | TPM2_VER0_ERRORS = { | ||
71 | 0x000: "TPM_RC_SUCCESS", | ||
72 | 0x030: "TPM_RC_BAD_TAG", | ||
73 | } | ||
74 | |||
75 | TPM2_VER1_ERRORS = { | ||
76 | 0x000: "TPM_RC_FAILURE", | ||
77 | 0x001: "TPM_RC_FAILURE", | ||
78 | 0x003: "TPM_RC_SEQUENCE", | ||
79 | 0x00B: "TPM_RC_PRIVATE", | ||
80 | 0x019: "TPM_RC_HMAC", | ||
81 | 0x020: "TPM_RC_DISABLED", | ||
82 | 0x021: "TPM_RC_EXCLUSIVE", | ||
83 | 0x024: "TPM_RC_AUTH_TYPE", | ||
84 | 0x025: "TPM_RC_AUTH_MISSING", | ||
85 | 0x026: "TPM_RC_POLICY", | ||
86 | 0x027: "TPM_RC_PCR", | ||
87 | 0x028: "TPM_RC_PCR_CHANGED", | ||
88 | 0x02D: "TPM_RC_UPGRADE", | ||
89 | 0x02E: "TPM_RC_TOO_MANY_CONTEXTS", | ||
90 | 0x02F: "TPM_RC_AUTH_UNAVAILABLE", | ||
91 | 0x030: "TPM_RC_REBOOT", | ||
92 | 0x031: "TPM_RC_UNBALANCED", | ||
93 | 0x042: "TPM_RC_COMMAND_SIZE", | ||
94 | 0x043: "TPM_RC_COMMAND_CODE", | ||
95 | 0x044: "TPM_RC_AUTHSIZE", | ||
96 | 0x045: "TPM_RC_AUTH_CONTEXT", | ||
97 | 0x046: "TPM_RC_NV_RANGE", | ||
98 | 0x047: "TPM_RC_NV_SIZE", | ||
99 | 0x048: "TPM_RC_NV_LOCKED", | ||
100 | 0x049: "TPM_RC_NV_AUTHORIZATION", | ||
101 | 0x04A: "TPM_RC_NV_UNINITIALIZED", | ||
102 | 0x04B: "TPM_RC_NV_SPACE", | ||
103 | 0x04C: "TPM_RC_NV_DEFINED", | ||
104 | 0x050: "TPM_RC_BAD_CONTEXT", | ||
105 | 0x051: "TPM_RC_CPHASH", | ||
106 | 0x052: "TPM_RC_PARENT", | ||
107 | 0x053: "TPM_RC_NEEDS_TEST", | ||
108 | 0x054: "TPM_RC_NO_RESULT", | ||
109 | 0x055: "TPM_RC_SENSITIVE", | ||
110 | 0x07F: "RC_MAX_FM0", | ||
111 | } | ||
112 | |||
113 | TPM2_FMT1_ERRORS = { | ||
114 | 0x001: "TPM_RC_ASYMMETRIC", | ||
115 | 0x002: "TPM_RC_ATTRIBUTES", | ||
116 | 0x003: "TPM_RC_HASH", | ||
117 | 0x004: "TPM_RC_VALUE", | ||
118 | 0x005: "TPM_RC_HIERARCHY", | ||
119 | 0x007: "TPM_RC_KEY_SIZE", | ||
120 | 0x008: "TPM_RC_MGF", | ||
121 | 0x009: "TPM_RC_MODE", | ||
122 | 0x00A: "TPM_RC_TYPE", | ||
123 | 0x00B: "TPM_RC_HANDLE", | ||
124 | 0x00C: "TPM_RC_KDF", | ||
125 | 0x00D: "TPM_RC_RANGE", | ||
126 | 0x00E: "TPM_RC_AUTH_FAIL", | ||
127 | 0x00F: "TPM_RC_NONCE", | ||
128 | 0x010: "TPM_RC_PP", | ||
129 | 0x012: "TPM_RC_SCHEME", | ||
130 | 0x015: "TPM_RC_SIZE", | ||
131 | 0x016: "TPM_RC_SYMMETRIC", | ||
132 | 0x017: "TPM_RC_TAG", | ||
133 | 0x018: "TPM_RC_SELECTOR", | ||
134 | 0x01A: "TPM_RC_INSUFFICIENT", | ||
135 | 0x01B: "TPM_RC_SIGNATURE", | ||
136 | 0x01C: "TPM_RC_KEY", | ||
137 | 0x01D: "TPM_RC_POLICY_FAIL", | ||
138 | 0x01F: "TPM_RC_INTEGRITY", | ||
139 | 0x020: "TPM_RC_TICKET", | ||
140 | 0x021: "TPM_RC_RESERVED_BITS", | ||
141 | 0x022: "TPM_RC_BAD_AUTH", | ||
142 | 0x023: "TPM_RC_EXPIRED", | ||
143 | 0x024: "TPM_RC_POLICY_CC", | ||
144 | 0x025: "TPM_RC_BINDING", | ||
145 | 0x026: "TPM_RC_CURVE", | ||
146 | 0x027: "TPM_RC_ECC_POINT", | ||
147 | } | ||
148 | |||
149 | TPM2_WARN_ERRORS = { | ||
150 | 0x001: "TPM_RC_CONTEXT_GAP", | ||
151 | 0x002: "TPM_RC_OBJECT_MEMORY", | ||
152 | 0x003: "TPM_RC_SESSION_MEMORY", | ||
153 | 0x004: "TPM_RC_MEMORY", | ||
154 | 0x005: "TPM_RC_SESSION_HANDLES", | ||
155 | 0x006: "TPM_RC_OBJECT_HANDLES", | ||
156 | 0x007: "TPM_RC_LOCALITY", | ||
157 | 0x008: "TPM_RC_YIELDED", | ||
158 | 0x009: "TPM_RC_CANCELED", | ||
159 | 0x00A: "TPM_RC_TESTING", | ||
160 | 0x010: "TPM_RC_REFERENCE_H0", | ||
161 | 0x011: "TPM_RC_REFERENCE_H1", | ||
162 | 0x012: "TPM_RC_REFERENCE_H2", | ||
163 | 0x013: "TPM_RC_REFERENCE_H3", | ||
164 | 0x014: "TPM_RC_REFERENCE_H4", | ||
165 | 0x015: "TPM_RC_REFERENCE_H5", | ||
166 | 0x016: "TPM_RC_REFERENCE_H6", | ||
167 | 0x018: "TPM_RC_REFERENCE_S0", | ||
168 | 0x019: "TPM_RC_REFERENCE_S1", | ||
169 | 0x01A: "TPM_RC_REFERENCE_S2", | ||
170 | 0x01B: "TPM_RC_REFERENCE_S3", | ||
171 | 0x01C: "TPM_RC_REFERENCE_S4", | ||
172 | 0x01D: "TPM_RC_REFERENCE_S5", | ||
173 | 0x01E: "TPM_RC_REFERENCE_S6", | ||
174 | 0x020: "TPM_RC_NV_RATE", | ||
175 | 0x021: "TPM_RC_LOCKOUT", | ||
176 | 0x022: "TPM_RC_RETRY", | ||
177 | 0x023: "TPM_RC_NV_UNAVAILABLE", | ||
178 | 0x7F: "TPM_RC_NOT_USED", | ||
179 | } | ||
180 | |||
181 | RC_VER1 = 0x100 | ||
182 | RC_FMT1 = 0x080 | ||
183 | RC_WARN = 0x900 | ||
184 | |||
185 | ALG_DIGEST_SIZE_MAP = { | ||
186 | TPM2_ALG_SHA1: SHA1_DIGEST_SIZE, | ||
187 | TPM2_ALG_SHA256: SHA256_DIGEST_SIZE, | ||
188 | } | ||
189 | |||
190 | ALG_HASH_FUNCTION_MAP = { | ||
191 | TPM2_ALG_SHA1: hashlib.sha1, | ||
192 | TPM2_ALG_SHA256: hashlib.sha256 | ||
193 | } | ||
194 | |||
195 | NAME_ALG_MAP = { | ||
196 | "sha1": TPM2_ALG_SHA1, | ||
197 | "sha256": TPM2_ALG_SHA256, | ||
198 | } | ||
199 | |||
200 | |||
201 | class UnknownAlgorithmIdError(Exception): | ||
202 | def __init__(self, alg): | ||
203 | self.alg = alg | ||
204 | |||
205 | def __str__(self): | ||
206 | return '0x%0x' % (alg) | ||
207 | |||
208 | |||
209 | class UnknownAlgorithmNameError(Exception): | ||
210 | def __init__(self, name): | ||
211 | self.name = name | ||
212 | |||
213 | def __str__(self): | ||
214 | return name | ||
215 | |||
216 | |||
217 | class UnknownPCRBankError(Exception): | ||
218 | def __init__(self, alg): | ||
219 | self.alg = alg | ||
220 | |||
221 | def __str__(self): | ||
222 | return '0x%0x' % (alg) | ||
223 | |||
224 | |||
225 | class ProtocolError(Exception): | ||
226 | def __init__(self, cc, rc): | ||
227 | self.cc = cc | ||
228 | self.rc = rc | ||
229 | |||
230 | if (rc & RC_FMT1) == RC_FMT1: | ||
231 | self.name = TPM2_FMT1_ERRORS.get(rc & 0x3f, "TPM_RC_UNKNOWN") | ||
232 | elif (rc & RC_WARN) == RC_WARN: | ||
233 | self.name = TPM2_WARN_ERRORS.get(rc & 0x7f, "TPM_RC_UNKNOWN") | ||
234 | elif (rc & RC_VER1) == RC_VER1: | ||
235 | self.name = TPM2_VER1_ERRORS.get(rc & 0x7f, "TPM_RC_UNKNOWN") | ||
236 | else: | ||
237 | self.name = TPM2_VER0_ERRORS.get(rc & 0x7f, "TPM_RC_UNKNOWN") | ||
238 | |||
239 | def __str__(self): | ||
240 | if self.cc: | ||
241 | return '%s: cc=0x%08x, rc=0x%08x' % (self.name, self.cc, self.rc) | ||
242 | else: | ||
243 | return '%s: rc=0x%08x' % (self.name, self.rc) | ||
244 | |||
245 | |||
246 | class AuthCommand(object): | ||
247 | """TPMS_AUTH_COMMAND""" | ||
248 | |||
249 | def __init__(self, session_handle=TPM2_RS_PW, nonce='', session_attributes=0, | ||
250 | hmac=''): | ||
251 | self.session_handle = session_handle | ||
252 | self.nonce = nonce | ||
253 | self.session_attributes = session_attributes | ||
254 | self.hmac = hmac | ||
255 | |||
256 | def __str__(self): | ||
257 | fmt = '>I H%us B H%us' % (len(self.nonce), len(self.hmac)) | ||
258 | return struct.pack(fmt, self.session_handle, len(self.nonce), | ||
259 | self.nonce, self.session_attributes, len(self.hmac), | ||
260 | self.hmac) | ||
261 | |||
262 | def __len__(self): | ||
263 | fmt = '>I H%us B H%us' % (len(self.nonce), len(self.hmac)) | ||
264 | return struct.calcsize(fmt) | ||
265 | |||
266 | |||
267 | class SensitiveCreate(object): | ||
268 | """TPMS_SENSITIVE_CREATE""" | ||
269 | |||
270 | def __init__(self, user_auth='', data=''): | ||
271 | self.user_auth = user_auth | ||
272 | self.data = data | ||
273 | |||
274 | def __str__(self): | ||
275 | fmt = '>H%us H%us' % (len(self.user_auth), len(self.data)) | ||
276 | return struct.pack(fmt, len(self.user_auth), self.user_auth, | ||
277 | len(self.data), self.data) | ||
278 | |||
279 | def __len__(self): | ||
280 | fmt = '>H%us H%us' % (len(self.user_auth), len(self.data)) | ||
281 | return struct.calcsize(fmt) | ||
282 | |||
283 | |||
284 | class Public(object): | ||
285 | """TPMT_PUBLIC""" | ||
286 | |||
287 | FIXED_TPM = (1 << 1) | ||
288 | FIXED_PARENT = (1 << 4) | ||
289 | SENSITIVE_DATA_ORIGIN = (1 << 5) | ||
290 | USER_WITH_AUTH = (1 << 6) | ||
291 | RESTRICTED = (1 << 16) | ||
292 | DECRYPT = (1 << 17) | ||
293 | |||
294 | def __fmt(self): | ||
295 | return '>HHIH%us%usH%us' % \ | ||
296 | (len(self.auth_policy), len(self.parameters), len(self.unique)) | ||
297 | |||
298 | def __init__(self, object_type, name_alg, object_attributes, auth_policy='', | ||
299 | parameters='', unique=''): | ||
300 | self.object_type = object_type | ||
301 | self.name_alg = name_alg | ||
302 | self.object_attributes = object_attributes | ||
303 | self.auth_policy = auth_policy | ||
304 | self.parameters = parameters | ||
305 | self.unique = unique | ||
306 | |||
307 | def __str__(self): | ||
308 | return struct.pack(self.__fmt(), | ||
309 | self.object_type, | ||
310 | self.name_alg, | ||
311 | self.object_attributes, | ||
312 | len(self.auth_policy), | ||
313 | self.auth_policy, | ||
314 | self.parameters, | ||
315 | len(self.unique), | ||
316 | self.unique) | ||
317 | |||
318 | def __len__(self): | ||
319 | return struct.calcsize(self.__fmt()) | ||
320 | |||
321 | |||
322 | def get_digest_size(alg): | ||
323 | ds = ALG_DIGEST_SIZE_MAP.get(alg) | ||
324 | if not ds: | ||
325 | raise UnknownAlgorithmIdError(alg) | ||
326 | return ds | ||
327 | |||
328 | |||
329 | def get_hash_function(alg): | ||
330 | f = ALG_HASH_FUNCTION_MAP.get(alg) | ||
331 | if not f: | ||
332 | raise UnknownAlgorithmIdError(alg) | ||
333 | return f | ||
334 | |||
335 | |||
336 | def get_algorithm(name): | ||
337 | alg = NAME_ALG_MAP.get(name) | ||
338 | if not alg: | ||
339 | raise UnknownAlgorithmNameError(name) | ||
340 | return alg | ||
341 | |||
342 | |||
343 | def hex_dump(d): | ||
344 | d = [format(ord(x), '02x') for x in d] | ||
345 | d = [d[i: i + 16] for i in xrange(0, len(d), 16)] | ||
346 | d = [' '.join(x) for x in d] | ||
347 | d = os.linesep.join(d) | ||
348 | |||
349 | return d | ||
350 | |||
351 | class Client: | ||
352 | FLAG_DEBUG = 0x01 | ||
353 | FLAG_SPACE = 0x02 | ||
354 | TPM_IOC_NEW_SPACE = 0xa200 | ||
355 | |||
356 | def __init__(self, flags = 0): | ||
357 | self.flags = flags | ||
358 | |||
359 | if (self.flags & Client.FLAG_SPACE) == 0: | ||
360 | self.tpm = open('/dev/tpm0', 'r+b') | ||
361 | else: | ||
362 | self.tpm = open('/dev/tpmrm0', 'r+b') | ||
363 | |||
364 | def close(self): | ||
365 | self.tpm.close() | ||
366 | |||
367 | def send_cmd(self, cmd): | ||
368 | self.tpm.write(cmd) | ||
369 | rsp = self.tpm.read() | ||
370 | |||
371 | if (self.flags & Client.FLAG_DEBUG) != 0: | ||
372 | sys.stderr.write('cmd' + os.linesep) | ||
373 | sys.stderr.write(hex_dump(cmd) + os.linesep) | ||
374 | sys.stderr.write('rsp' + os.linesep) | ||
375 | sys.stderr.write(hex_dump(rsp) + os.linesep) | ||
376 | |||
377 | rc = struct.unpack('>I', rsp[6:10])[0] | ||
378 | if rc != 0: | ||
379 | cc = struct.unpack('>I', cmd[6:10])[0] | ||
380 | raise ProtocolError(cc, rc) | ||
381 | |||
382 | return rsp | ||
383 | |||
384 | def read_pcr(self, i, bank_alg = TPM2_ALG_SHA1): | ||
385 | pcrsel_len = max((i >> 3) + 1, 3) | ||
386 | pcrsel = [0] * pcrsel_len | ||
387 | pcrsel[i >> 3] = 1 << (i & 7) | ||
388 | pcrsel = ''.join(map(chr, pcrsel)) | ||
389 | |||
390 | fmt = '>HII IHB%us' % (pcrsel_len) | ||
391 | cmd = struct.pack(fmt, | ||
392 | TPM2_ST_NO_SESSIONS, | ||
393 | struct.calcsize(fmt), | ||
394 | TPM2_CC_PCR_READ, | ||
395 | 1, | ||
396 | bank_alg, | ||
397 | pcrsel_len, pcrsel) | ||
398 | |||
399 | rsp = self.send_cmd(cmd) | ||
400 | |||
401 | pcr_update_cnt, pcr_select_cnt = struct.unpack('>II', rsp[10:18]) | ||
402 | assert pcr_select_cnt == 1 | ||
403 | rsp = rsp[18:] | ||
404 | |||
405 | alg2, pcrsel_len2 = struct.unpack('>HB', rsp[:3]) | ||
406 | assert bank_alg == alg2 and pcrsel_len == pcrsel_len2 | ||
407 | rsp = rsp[3 + pcrsel_len:] | ||
408 | |||
409 | digest_cnt = struct.unpack('>I', rsp[:4])[0] | ||
410 | if digest_cnt == 0: | ||
411 | return None | ||
412 | rsp = rsp[6:] | ||
413 | |||
414 | return rsp | ||
415 | |||
416 | def extend_pcr(self, i, dig, bank_alg = TPM2_ALG_SHA1): | ||
417 | ds = get_digest_size(bank_alg) | ||
418 | assert(ds == len(dig)) | ||
419 | |||
420 | auth_cmd = AuthCommand() | ||
421 | |||
422 | fmt = '>HII I I%us IH%us' % (len(auth_cmd), ds) | ||
423 | cmd = struct.pack( | ||
424 | fmt, | ||
425 | TPM2_ST_SESSIONS, | ||
426 | struct.calcsize(fmt), | ||
427 | TPM2_CC_PCR_EXTEND, | ||
428 | i, | ||
429 | len(auth_cmd), | ||
430 | str(auth_cmd), | ||
431 | 1, bank_alg, dig) | ||
432 | |||
433 | self.send_cmd(cmd) | ||
434 | |||
435 | def start_auth_session(self, session_type, name_alg = TPM2_ALG_SHA1): | ||
436 | fmt = '>HII IIH16sHBHH' | ||
437 | cmd = struct.pack(fmt, | ||
438 | TPM2_ST_NO_SESSIONS, | ||
439 | struct.calcsize(fmt), | ||
440 | TPM2_CC_START_AUTH_SESSION, | ||
441 | TPM2_RH_NULL, | ||
442 | TPM2_RH_NULL, | ||
443 | 16, | ||
444 | '\0' * 16, | ||
445 | 0, | ||
446 | session_type, | ||
447 | TPM2_ALG_NULL, | ||
448 | name_alg) | ||
449 | |||
450 | return struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] | ||
451 | |||
452 | def __calc_pcr_digest(self, pcrs, bank_alg = TPM2_ALG_SHA1, | ||
453 | digest_alg = TPM2_ALG_SHA1): | ||
454 | x = [] | ||
455 | f = get_hash_function(digest_alg) | ||
456 | |||
457 | for i in pcrs: | ||
458 | pcr = self.read_pcr(i, bank_alg) | ||
459 | if pcr == None: | ||
460 | return None | ||
461 | x += pcr | ||
462 | |||
463 | return f(bytearray(x)).digest() | ||
464 | |||
465 | def policy_pcr(self, handle, pcrs, bank_alg = TPM2_ALG_SHA1, | ||
466 | name_alg = TPM2_ALG_SHA1): | ||
467 | ds = get_digest_size(name_alg) | ||
468 | dig = self.__calc_pcr_digest(pcrs, bank_alg, name_alg) | ||
469 | if not dig: | ||
470 | raise UnknownPCRBankError(bank_alg) | ||
471 | |||
472 | pcrsel_len = max((max(pcrs) >> 3) + 1, 3) | ||
473 | pcrsel = [0] * pcrsel_len | ||
474 | for i in pcrs: | ||
475 | pcrsel[i >> 3] |= 1 << (i & 7) | ||
476 | pcrsel = ''.join(map(chr, pcrsel)) | ||
477 | |||
478 | fmt = '>HII IH%usIHB3s' % ds | ||
479 | cmd = struct.pack(fmt, | ||
480 | TPM2_ST_NO_SESSIONS, | ||
481 | struct.calcsize(fmt), | ||
482 | TPM2_CC_POLICY_PCR, | ||
483 | handle, | ||
484 | len(dig), str(dig), | ||
485 | 1, | ||
486 | bank_alg, | ||
487 | pcrsel_len, pcrsel) | ||
488 | |||
489 | self.send_cmd(cmd) | ||
490 | |||
491 | def policy_password(self, handle): | ||
492 | fmt = '>HII I' | ||
493 | cmd = struct.pack(fmt, | ||
494 | TPM2_ST_NO_SESSIONS, | ||
495 | struct.calcsize(fmt), | ||
496 | TPM2_CC_POLICY_PASSWORD, | ||
497 | handle) | ||
498 | |||
499 | self.send_cmd(cmd) | ||
500 | |||
501 | def get_policy_digest(self, handle): | ||
502 | fmt = '>HII I' | ||
503 | cmd = struct.pack(fmt, | ||
504 | TPM2_ST_NO_SESSIONS, | ||
505 | struct.calcsize(fmt), | ||
506 | TPM2_CC_POLICY_GET_DIGEST, | ||
507 | handle) | ||
508 | |||
509 | return self.send_cmd(cmd)[12:] | ||
510 | |||
511 | def flush_context(self, handle): | ||
512 | fmt = '>HIII' | ||
513 | cmd = struct.pack(fmt, | ||
514 | TPM2_ST_NO_SESSIONS, | ||
515 | struct.calcsize(fmt), | ||
516 | TPM2_CC_FLUSH_CONTEXT, | ||
517 | handle) | ||
518 | |||
519 | self.send_cmd(cmd) | ||
520 | |||
521 | def create_root_key(self, auth_value = ''): | ||
522 | attributes = \ | ||
523 | Public.FIXED_TPM | \ | ||
524 | Public.FIXED_PARENT | \ | ||
525 | Public.SENSITIVE_DATA_ORIGIN | \ | ||
526 | Public.USER_WITH_AUTH | \ | ||
527 | Public.RESTRICTED | \ | ||
528 | Public.DECRYPT | ||
529 | |||
530 | auth_cmd = AuthCommand() | ||
531 | sensitive = SensitiveCreate(user_auth=auth_value) | ||
532 | |||
533 | public_parms = struct.pack( | ||
534 | '>HHHHHI', | ||
535 | TPM2_ALG_AES, | ||
536 | 128, | ||
537 | TPM2_ALG_CFB, | ||
538 | TPM2_ALG_NULL, | ||
539 | 2048, | ||
540 | 0) | ||
541 | |||
542 | public = Public( | ||
543 | object_type=TPM2_ALG_RSA, | ||
544 | name_alg=TPM2_ALG_SHA1, | ||
545 | object_attributes=attributes, | ||
546 | parameters=public_parms) | ||
547 | |||
548 | fmt = '>HIII I%us H%us H%us HI' % \ | ||
549 | (len(auth_cmd), len(sensitive), len(public)) | ||
550 | cmd = struct.pack( | ||
551 | fmt, | ||
552 | TPM2_ST_SESSIONS, | ||
553 | struct.calcsize(fmt), | ||
554 | TPM2_CC_CREATE_PRIMARY, | ||
555 | TPM2_RH_OWNER, | ||
556 | len(auth_cmd), | ||
557 | str(auth_cmd), | ||
558 | len(sensitive), | ||
559 | str(sensitive), | ||
560 | len(public), | ||
561 | str(public), | ||
562 | 0, 0) | ||
563 | |||
564 | return struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] | ||
565 | |||
566 | def seal(self, parent_key, data, auth_value, policy_dig, | ||
567 | name_alg = TPM2_ALG_SHA1): | ||
568 | ds = get_digest_size(name_alg) | ||
569 | assert(not policy_dig or ds == len(policy_dig)) | ||
570 | |||
571 | attributes = 0 | ||
572 | if not policy_dig: | ||
573 | attributes |= Public.USER_WITH_AUTH | ||
574 | policy_dig = '' | ||
575 | |||
576 | auth_cmd = AuthCommand() | ||
577 | sensitive = SensitiveCreate(user_auth=auth_value, data=data) | ||
578 | |||
579 | public = Public( | ||
580 | object_type=TPM2_ALG_KEYEDHASH, | ||
581 | name_alg=name_alg, | ||
582 | object_attributes=attributes, | ||
583 | auth_policy=policy_dig, | ||
584 | parameters=struct.pack('>H', TPM2_ALG_NULL)) | ||
585 | |||
586 | fmt = '>HIII I%us H%us H%us HI' % \ | ||
587 | (len(auth_cmd), len(sensitive), len(public)) | ||
588 | cmd = struct.pack( | ||
589 | fmt, | ||
590 | TPM2_ST_SESSIONS, | ||
591 | struct.calcsize(fmt), | ||
592 | TPM2_CC_CREATE, | ||
593 | parent_key, | ||
594 | len(auth_cmd), | ||
595 | str(auth_cmd), | ||
596 | len(sensitive), | ||
597 | str(sensitive), | ||
598 | len(public), | ||
599 | str(public), | ||
600 | 0, 0) | ||
601 | |||
602 | rsp = self.send_cmd(cmd) | ||
603 | |||
604 | return rsp[14:] | ||
605 | |||
606 | def unseal(self, parent_key, blob, auth_value, policy_handle): | ||
607 | private_len = struct.unpack('>H', blob[0:2])[0] | ||
608 | public_start = private_len + 2 | ||
609 | public_len = struct.unpack('>H', blob[public_start:public_start + 2])[0] | ||
610 | blob = blob[:private_len + public_len + 4] | ||
611 | |||
612 | auth_cmd = AuthCommand() | ||
613 | |||
614 | fmt = '>HII I I%us %us' % (len(auth_cmd), len(blob)) | ||
615 | cmd = struct.pack( | ||
616 | fmt, | ||
617 | TPM2_ST_SESSIONS, | ||
618 | struct.calcsize(fmt), | ||
619 | TPM2_CC_LOAD, | ||
620 | parent_key, | ||
621 | len(auth_cmd), | ||
622 | str(auth_cmd), | ||
623 | blob) | ||
624 | |||
625 | data_handle = struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] | ||
626 | |||
627 | if policy_handle: | ||
628 | auth_cmd = AuthCommand(session_handle=policy_handle, hmac=auth_value) | ||
629 | else: | ||
630 | auth_cmd = AuthCommand(hmac=auth_value) | ||
631 | |||
632 | fmt = '>HII I I%us' % (len(auth_cmd)) | ||
633 | cmd = struct.pack( | ||
634 | fmt, | ||
635 | TPM2_ST_SESSIONS, | ||
636 | struct.calcsize(fmt), | ||
637 | TPM2_CC_UNSEAL, | ||
638 | data_handle, | ||
639 | len(auth_cmd), | ||
640 | str(auth_cmd)) | ||
641 | |||
642 | try: | ||
643 | rsp = self.send_cmd(cmd) | ||
644 | finally: | ||
645 | self.flush_context(data_handle) | ||
646 | |||
647 | data_len = struct.unpack('>I', rsp[10:14])[0] - 2 | ||
648 | |||
649 | return rsp[16:16 + data_len] | ||
650 | |||
651 | def reset_da_lock(self): | ||
652 | auth_cmd = AuthCommand() | ||
653 | |||
654 | fmt = '>HII I I%us' % (len(auth_cmd)) | ||
655 | cmd = struct.pack( | ||
656 | fmt, | ||
657 | TPM2_ST_SESSIONS, | ||
658 | struct.calcsize(fmt), | ||
659 | TPM2_CC_DICTIONARY_ATTACK_LOCK_RESET, | ||
660 | TPM2_RH_LOCKOUT, | ||
661 | len(auth_cmd), | ||
662 | str(auth_cmd)) | ||
663 | |||
664 | self.send_cmd(cmd) | ||
665 | |||
666 | def __get_cap_cnt(self, cap, pt, cnt): | ||
667 | handles = [] | ||
668 | fmt = '>HII III' | ||
669 | |||
670 | cmd = struct.pack(fmt, | ||
671 | TPM2_ST_NO_SESSIONS, | ||
672 | struct.calcsize(fmt), | ||
673 | TPM2_CC_GET_CAPABILITY, | ||
674 | cap, pt, cnt) | ||
675 | |||
676 | rsp = self.send_cmd(cmd)[10:] | ||
677 | more_data, cap, cnt = struct.unpack('>BII', rsp[:9]) | ||
678 | rsp = rsp[9:] | ||
679 | |||
680 | for i in xrange(0, cnt): | ||
681 | handle = struct.unpack('>I', rsp[:4])[0] | ||
682 | handles.append(handle) | ||
683 | rsp = rsp[4:] | ||
684 | |||
685 | return handles, more_data | ||
686 | |||
687 | def get_cap(self, cap, pt): | ||
688 | handles = [] | ||
689 | |||
690 | more_data = True | ||
691 | while more_data: | ||
692 | next_handles, more_data = self.__get_cap_cnt(cap, pt, 1) | ||
693 | handles += next_handles | ||
694 | pt += 1 | ||
695 | |||
696 | return handles | ||
diff --git a/tools/testing/selftests/tpm2/tpm2_tests.py b/tools/testing/selftests/tpm2/tpm2_tests.py new file mode 100644 index 000000000000..3bb066fea4a0 --- /dev/null +++ b/tools/testing/selftests/tpm2/tpm2_tests.py | |||
@@ -0,0 +1,227 @@ | |||
1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | ||
2 | |||
3 | from argparse import ArgumentParser | ||
4 | from argparse import FileType | ||
5 | import os | ||
6 | import sys | ||
7 | import tpm2 | ||
8 | from tpm2 import ProtocolError | ||
9 | import unittest | ||
10 | import logging | ||
11 | import struct | ||
12 | |||
13 | class SmokeTest(unittest.TestCase): | ||
14 | def setUp(self): | ||
15 | self.client = tpm2.Client() | ||
16 | self.root_key = self.client.create_root_key() | ||
17 | |||
18 | def tearDown(self): | ||
19 | self.client.flush_context(self.root_key) | ||
20 | self.client.close() | ||
21 | |||
22 | def test_seal_with_auth(self): | ||
23 | data = 'X' * 64 | ||
24 | auth = 'A' * 15 | ||
25 | |||
26 | blob = self.client.seal(self.root_key, data, auth, None) | ||
27 | result = self.client.unseal(self.root_key, blob, auth, None) | ||
28 | self.assertEqual(data, result) | ||
29 | |||
30 | def test_seal_with_policy(self): | ||
31 | handle = self.client.start_auth_session(tpm2.TPM2_SE_TRIAL) | ||
32 | |||
33 | data = 'X' * 64 | ||
34 | auth = 'A' * 15 | ||
35 | pcrs = [16] | ||
36 | |||
37 | try: | ||
38 | self.client.policy_pcr(handle, pcrs) | ||
39 | self.client.policy_password(handle) | ||
40 | |||
41 | policy_dig = self.client.get_policy_digest(handle) | ||
42 | finally: | ||
43 | self.client.flush_context(handle) | ||
44 | |||
45 | blob = self.client.seal(self.root_key, data, auth, policy_dig) | ||
46 | |||
47 | handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) | ||
48 | |||
49 | try: | ||
50 | self.client.policy_pcr(handle, pcrs) | ||
51 | self.client.policy_password(handle) | ||
52 | |||
53 | result = self.client.unseal(self.root_key, blob, auth, handle) | ||
54 | except: | ||
55 | self.client.flush_context(handle) | ||
56 | raise | ||
57 | |||
58 | self.assertEqual(data, result) | ||
59 | |||
60 | def test_unseal_with_wrong_auth(self): | ||
61 | data = 'X' * 64 | ||
62 | auth = 'A' * 20 | ||
63 | rc = 0 | ||
64 | |||
65 | blob = self.client.seal(self.root_key, data, auth, None) | ||
66 | try: | ||
67 | result = self.client.unseal(self.root_key, blob, auth[:-1] + 'B', None) | ||
68 | except ProtocolError, e: | ||
69 | rc = e.rc | ||
70 | |||
71 | self.assertEqual(rc, tpm2.TPM2_RC_AUTH_FAIL) | ||
72 | |||
73 | def test_unseal_with_wrong_policy(self): | ||
74 | handle = self.client.start_auth_session(tpm2.TPM2_SE_TRIAL) | ||
75 | |||
76 | data = 'X' * 64 | ||
77 | auth = 'A' * 17 | ||
78 | pcrs = [16] | ||
79 | |||
80 | try: | ||
81 | self.client.policy_pcr(handle, pcrs) | ||
82 | self.client.policy_password(handle) | ||
83 | |||
84 | policy_dig = self.client.get_policy_digest(handle) | ||
85 | finally: | ||
86 | self.client.flush_context(handle) | ||
87 | |||
88 | blob = self.client.seal(self.root_key, data, auth, policy_dig) | ||
89 | |||
90 | # Extend first a PCR that is not part of the policy and try to unseal. | ||
91 | # This should succeed. | ||
92 | |||
93 | ds = tpm2.get_digest_size(tpm2.TPM2_ALG_SHA1) | ||
94 | self.client.extend_pcr(1, 'X' * ds) | ||
95 | |||
96 | handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) | ||
97 | |||
98 | try: | ||
99 | self.client.policy_pcr(handle, pcrs) | ||
100 | self.client.policy_password(handle) | ||
101 | |||
102 | result = self.client.unseal(self.root_key, blob, auth, handle) | ||
103 | except: | ||
104 | self.client.flush_context(handle) | ||
105 | raise | ||
106 | |||
107 | self.assertEqual(data, result) | ||
108 | |||
109 | # Then, extend a PCR that is part of the policy and try to unseal. | ||
110 | # This should fail. | ||
111 | self.client.extend_pcr(16, 'X' * ds) | ||
112 | |||
113 | handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) | ||
114 | |||
115 | rc = 0 | ||
116 | |||
117 | try: | ||
118 | self.client.policy_pcr(handle, pcrs) | ||
119 | self.client.policy_password(handle) | ||
120 | |||
121 | result = self.client.unseal(self.root_key, blob, auth, handle) | ||
122 | except ProtocolError, e: | ||
123 | rc = e.rc | ||
124 | self.client.flush_context(handle) | ||
125 | except: | ||
126 | self.client.flush_context(handle) | ||
127 | raise | ||
128 | |||
129 | self.assertEqual(rc, tpm2.TPM2_RC_POLICY_FAIL) | ||
130 | |||
131 | def test_seal_with_too_long_auth(self): | ||
132 | ds = tpm2.get_digest_size(tpm2.TPM2_ALG_SHA1) | ||
133 | data = 'X' * 64 | ||
134 | auth = 'A' * (ds + 1) | ||
135 | |||
136 | rc = 0 | ||
137 | try: | ||
138 | blob = self.client.seal(self.root_key, data, auth, None) | ||
139 | except ProtocolError, e: | ||
140 | rc = e.rc | ||
141 | |||
142 | self.assertEqual(rc, tpm2.TPM2_RC_SIZE) | ||
143 | |||
144 | def test_too_short_cmd(self): | ||
145 | rejected = False | ||
146 | try: | ||
147 | fmt = '>HIII' | ||
148 | cmd = struct.pack(fmt, | ||
149 | tpm2.TPM2_ST_NO_SESSIONS, | ||
150 | struct.calcsize(fmt) + 1, | ||
151 | tpm2.TPM2_CC_FLUSH_CONTEXT, | ||
152 | 0xDEADBEEF) | ||
153 | |||
154 | self.client.send_cmd(cmd) | ||
155 | except IOError, e: | ||
156 | rejected = True | ||
157 | except: | ||
158 | pass | ||
159 | self.assertEqual(rejected, True) | ||
160 | |||
161 | class SpaceTest(unittest.TestCase): | ||
162 | def setUp(self): | ||
163 | logging.basicConfig(filename='SpaceTest.log', level=logging.DEBUG) | ||
164 | |||
165 | def test_make_two_spaces(self): | ||
166 | log = logging.getLogger(__name__) | ||
167 | log.debug("test_make_two_spaces") | ||
168 | |||
169 | space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) | ||
170 | root1 = space1.create_root_key() | ||
171 | space2 = tpm2.Client(tpm2.Client.FLAG_SPACE) | ||
172 | root2 = space2.create_root_key() | ||
173 | root3 = space2.create_root_key() | ||
174 | |||
175 | log.debug("%08x" % (root1)) | ||
176 | log.debug("%08x" % (root2)) | ||
177 | log.debug("%08x" % (root3)) | ||
178 | |||
179 | def test_flush_context(self): | ||
180 | log = logging.getLogger(__name__) | ||
181 | log.debug("test_flush_context") | ||
182 | |||
183 | space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) | ||
184 | root1 = space1.create_root_key() | ||
185 | log.debug("%08x" % (root1)) | ||
186 | |||
187 | space1.flush_context(root1) | ||
188 | |||
189 | def test_get_handles(self): | ||
190 | log = logging.getLogger(__name__) | ||
191 | log.debug("test_get_handles") | ||
192 | |||
193 | space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) | ||
194 | space1.create_root_key() | ||
195 | space2 = tpm2.Client(tpm2.Client.FLAG_SPACE) | ||
196 | space2.create_root_key() | ||
197 | space2.create_root_key() | ||
198 | |||
199 | handles = space2.get_cap(tpm2.TPM2_CAP_HANDLES, tpm2.HR_TRANSIENT) | ||
200 | |||
201 | self.assertEqual(len(handles), 2) | ||
202 | |||
203 | log.debug("%08x" % (handles[0])) | ||
204 | log.debug("%08x" % (handles[1])) | ||
205 | |||
206 | def test_invalid_cc(self): | ||
207 | log = logging.getLogger(__name__) | ||
208 | log.debug(sys._getframe().f_code.co_name) | ||
209 | |||
210 | TPM2_CC_INVALID = tpm2.TPM2_CC_FIRST - 1 | ||
211 | |||
212 | space1 = tpm2.Client(tpm2.Client.FLAG_SPACE) | ||
213 | root1 = space1.create_root_key() | ||
214 | log.debug("%08x" % (root1)) | ||
215 | |||
216 | fmt = '>HII' | ||
217 | cmd = struct.pack(fmt, tpm2.TPM2_ST_NO_SESSIONS, struct.calcsize(fmt), | ||
218 | TPM2_CC_INVALID) | ||
219 | |||
220 | rc = 0 | ||
221 | try: | ||
222 | space1.send_cmd(cmd) | ||
223 | except ProtocolError, e: | ||
224 | rc = e.rc | ||
225 | |||
226 | self.assertEqual(rc, tpm2.TPM2_RC_COMMAND_CODE | | ||
227 | tpm2.TSS2_RESMGR_TPM_RC_LAYER) | ||