Commit | Line | Data |
---|---|---|
2e19e101 SG |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Copyright (C) 2004 IBM Corporation | |
4 | * Copyright (C) 2014 Intel Corporation | |
5 | */ | |
6 | ||
7 | #include <linux/string.h> | |
8 | #include <linux/err.h> | |
9 | #include <linux/tpm.h> | |
10 | #include <linux/tpm_command.h> | |
11 | ||
12 | #include <keys/trusted-type.h> | |
13 | #include <keys/trusted_tpm.h> | |
14 | ||
15 | static struct tpm2_hash tpm2_hash_map[] = { | |
16 | {HASH_ALGO_SHA1, TPM_ALG_SHA1}, | |
17 | {HASH_ALGO_SHA256, TPM_ALG_SHA256}, | |
18 | {HASH_ALGO_SHA384, TPM_ALG_SHA384}, | |
19 | {HASH_ALGO_SHA512, TPM_ALG_SHA512}, | |
20 | {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, | |
21 | }; | |
22 | ||
23 | /** | |
24 | * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. | |
25 | * | |
26 | * @buf: an allocated tpm_buf instance | |
27 | * @session_handle: session handle | |
28 | * @nonce: the session nonce, may be NULL if not used | |
29 | * @nonce_len: the session nonce length, may be 0 if not used | |
30 | * @attributes: the session attributes | |
31 | * @hmac: the session HMAC or password, may be NULL if not used | |
32 | * @hmac_len: the session HMAC or password length, maybe 0 if not used | |
33 | */ | |
34 | static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, | |
35 | const u8 *nonce, u16 nonce_len, | |
36 | u8 attributes, | |
37 | const u8 *hmac, u16 hmac_len) | |
38 | { | |
39 | tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len); | |
40 | tpm_buf_append_u32(buf, session_handle); | |
41 | tpm_buf_append_u16(buf, nonce_len); | |
42 | ||
43 | if (nonce && nonce_len) | |
44 | tpm_buf_append(buf, nonce, nonce_len); | |
45 | ||
46 | tpm_buf_append_u8(buf, attributes); | |
47 | tpm_buf_append_u16(buf, hmac_len); | |
48 | ||
49 | if (hmac && hmac_len) | |
50 | tpm_buf_append(buf, hmac, hmac_len); | |
51 | } | |
52 | ||
53 | /** | |
54 | * tpm2_seal_trusted() - seal the payload of a trusted key | |
55 | * | |
56 | * @chip: TPM chip to use | |
57 | * @payload: the key data in clear and encrypted form | |
58 | * @options: authentication values and other options | |
59 | * | |
60 | * Return: < 0 on error and 0 on success. | |
61 | */ | |
62 | int tpm2_seal_trusted(struct tpm_chip *chip, | |
63 | struct trusted_key_payload *payload, | |
64 | struct trusted_key_options *options) | |
65 | { | |
66 | unsigned int blob_len; | |
67 | struct tpm_buf buf; | |
68 | u32 hash; | |
69 | int i; | |
70 | int rc; | |
71 | ||
72 | for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { | |
73 | if (options->hash == tpm2_hash_map[i].crypto_id) { | |
74 | hash = tpm2_hash_map[i].tpm_id; | |
75 | break; | |
76 | } | |
77 | } | |
78 | ||
79 | if (i == ARRAY_SIZE(tpm2_hash_map)) | |
80 | return -EINVAL; | |
81 | ||
82 | rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); | |
83 | if (rc) | |
84 | return rc; | |
85 | ||
8c657a05 JS |
86 | rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); |
87 | if (rc) { | |
88 | tpm_put_ops(chip); | |
89 | return rc; | |
90 | } | |
91 | ||
2e19e101 SG |
92 | tpm_buf_append_u32(&buf, options->keyhandle); |
93 | tpm2_buf_append_auth(&buf, TPM2_RS_PW, | |
94 | NULL /* nonce */, 0, | |
95 | 0 /* session_attributes */, | |
96 | options->keyauth /* hmac */, | |
97 | TPM_DIGEST_SIZE); | |
98 | ||
99 | /* sensitive */ | |
100 | tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1); | |
101 | ||
102 | tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); | |
103 | tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); | |
104 | tpm_buf_append_u16(&buf, payload->key_len + 1); | |
105 | tpm_buf_append(&buf, payload->key, payload->key_len); | |
106 | tpm_buf_append_u8(&buf, payload->migratable); | |
107 | ||
108 | /* public */ | |
109 | tpm_buf_append_u16(&buf, 14 + options->policydigest_len); | |
110 | tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); | |
111 | tpm_buf_append_u16(&buf, hash); | |
112 | ||
113 | /* policy */ | |
114 | if (options->policydigest_len) { | |
115 | tpm_buf_append_u32(&buf, 0); | |
116 | tpm_buf_append_u16(&buf, options->policydigest_len); | |
117 | tpm_buf_append(&buf, options->policydigest, | |
118 | options->policydigest_len); | |
119 | } else { | |
120 | tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH); | |
121 | tpm_buf_append_u16(&buf, 0); | |
122 | } | |
123 | ||
124 | /* public parameters */ | |
125 | tpm_buf_append_u16(&buf, TPM_ALG_NULL); | |
126 | tpm_buf_append_u16(&buf, 0); | |
127 | ||
128 | /* outside info */ | |
129 | tpm_buf_append_u16(&buf, 0); | |
130 | ||
131 | /* creation PCR */ | |
132 | tpm_buf_append_u32(&buf, 0); | |
133 | ||
134 | if (buf.flags & TPM_BUF_OVERFLOW) { | |
135 | rc = -E2BIG; | |
136 | goto out; | |
137 | } | |
138 | ||
8c657a05 | 139 | rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); |
2e19e101 SG |
140 | if (rc) |
141 | goto out; | |
142 | ||
143 | blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); | |
144 | if (blob_len > MAX_BLOB_SIZE) { | |
145 | rc = -E2BIG; | |
146 | goto out; | |
147 | } | |
148 | if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { | |
149 | rc = -EFAULT; | |
150 | goto out; | |
151 | } | |
152 | ||
153 | memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); | |
154 | payload->blob_len = blob_len; | |
155 | ||
156 | out: | |
157 | tpm_buf_destroy(&buf); | |
158 | ||
159 | if (rc > 0) { | |
160 | if (tpm2_rc_value(rc) == TPM2_RC_HASH) | |
161 | rc = -EINVAL; | |
162 | else | |
163 | rc = -EPERM; | |
164 | } | |
165 | ||
8c657a05 | 166 | tpm_put_ops(chip); |
2e19e101 SG |
167 | return rc; |
168 | } | |
169 | ||
170 | /** | |
171 | * tpm2_load_cmd() - execute a TPM2_Load command | |
172 | * | |
173 | * @chip: TPM chip to use | |
174 | * @payload: the key data in clear and encrypted form | |
175 | * @options: authentication values and other options | |
176 | * @blob_handle: returned blob handle | |
177 | * | |
178 | * Return: 0 on success. | |
179 | * -E2BIG on wrong payload size. | |
180 | * -EPERM on tpm error status. | |
181 | * < 0 error from tpm_send. | |
182 | */ | |
183 | static int tpm2_load_cmd(struct tpm_chip *chip, | |
184 | struct trusted_key_payload *payload, | |
185 | struct trusted_key_options *options, | |
186 | u32 *blob_handle) | |
187 | { | |
188 | struct tpm_buf buf; | |
189 | unsigned int private_len; | |
190 | unsigned int public_len; | |
191 | unsigned int blob_len; | |
192 | int rc; | |
193 | ||
194 | private_len = be16_to_cpup((__be16 *) &payload->blob[0]); | |
195 | if (private_len > (payload->blob_len - 2)) | |
196 | return -E2BIG; | |
197 | ||
198 | public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); | |
199 | blob_len = private_len + public_len + 4; | |
200 | if (blob_len > payload->blob_len) | |
201 | return -E2BIG; | |
202 | ||
203 | rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); | |
204 | if (rc) | |
205 | return rc; | |
206 | ||
207 | tpm_buf_append_u32(&buf, options->keyhandle); | |
208 | tpm2_buf_append_auth(&buf, TPM2_RS_PW, | |
209 | NULL /* nonce */, 0, | |
210 | 0 /* session_attributes */, | |
211 | options->keyauth /* hmac */, | |
212 | TPM_DIGEST_SIZE); | |
213 | ||
214 | tpm_buf_append(&buf, payload->blob, blob_len); | |
215 | ||
216 | if (buf.flags & TPM_BUF_OVERFLOW) { | |
217 | rc = -E2BIG; | |
218 | goto out; | |
219 | } | |
220 | ||
8c657a05 | 221 | rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); |
2e19e101 SG |
222 | if (!rc) |
223 | *blob_handle = be32_to_cpup( | |
224 | (__be32 *) &buf.data[TPM_HEADER_SIZE]); | |
225 | ||
226 | out: | |
227 | tpm_buf_destroy(&buf); | |
228 | ||
229 | if (rc > 0) | |
230 | rc = -EPERM; | |
231 | ||
232 | return rc; | |
233 | } | |
234 | ||
235 | /** | |
236 | * tpm2_unseal_cmd() - execute a TPM2_Unload command | |
237 | * | |
238 | * @chip: TPM chip to use | |
239 | * @payload: the key data in clear and encrypted form | |
240 | * @options: authentication values and other options | |
241 | * @blob_handle: blob handle | |
242 | * | |
243 | * Return: 0 on success | |
244 | * -EPERM on tpm error status | |
245 | * < 0 error from tpm_send | |
246 | */ | |
247 | static int tpm2_unseal_cmd(struct tpm_chip *chip, | |
248 | struct trusted_key_payload *payload, | |
249 | struct trusted_key_options *options, | |
250 | u32 blob_handle) | |
251 | { | |
252 | struct tpm_buf buf; | |
253 | u16 data_len; | |
254 | u8 *data; | |
255 | int rc; | |
256 | ||
257 | rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); | |
258 | if (rc) | |
259 | return rc; | |
260 | ||
261 | tpm_buf_append_u32(&buf, blob_handle); | |
262 | tpm2_buf_append_auth(&buf, | |
263 | options->policyhandle ? | |
264 | options->policyhandle : TPM2_RS_PW, | |
265 | NULL /* nonce */, 0, | |
266 | TPM2_SA_CONTINUE_SESSION, | |
267 | options->blobauth /* hmac */, | |
268 | TPM_DIGEST_SIZE); | |
269 | ||
8c657a05 | 270 | rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); |
2e19e101 SG |
271 | if (rc > 0) |
272 | rc = -EPERM; | |
273 | ||
274 | if (!rc) { | |
275 | data_len = be16_to_cpup( | |
276 | (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); | |
277 | if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) { | |
278 | rc = -EFAULT; | |
279 | goto out; | |
280 | } | |
281 | ||
282 | if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) { | |
283 | rc = -EFAULT; | |
284 | goto out; | |
285 | } | |
286 | data = &buf.data[TPM_HEADER_SIZE + 6]; | |
287 | ||
288 | memcpy(payload->key, data, data_len - 1); | |
289 | payload->key_len = data_len - 1; | |
290 | payload->migratable = data[data_len - 1]; | |
291 | } | |
292 | ||
293 | out: | |
294 | tpm_buf_destroy(&buf); | |
295 | return rc; | |
296 | } | |
297 | ||
298 | /** | |
299 | * tpm2_unseal_trusted() - unseal the payload of a trusted key | |
300 | * | |
301 | * @chip: TPM chip to use | |
302 | * @payload: the key data in clear and encrypted form | |
303 | * @options: authentication values and other options | |
304 | * | |
305 | * Return: Same as with tpm_send. | |
306 | */ | |
307 | int tpm2_unseal_trusted(struct tpm_chip *chip, | |
308 | struct trusted_key_payload *payload, | |
309 | struct trusted_key_options *options) | |
310 | { | |
311 | u32 blob_handle; | |
312 | int rc; | |
313 | ||
8c657a05 | 314 | rc = tpm_try_get_ops(chip); |
2e19e101 SG |
315 | if (rc) |
316 | return rc; | |
317 | ||
8c657a05 JS |
318 | rc = tpm2_load_cmd(chip, payload, options, &blob_handle); |
319 | if (rc) | |
320 | goto out; | |
321 | ||
2e19e101 | 322 | rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); |
45477b3f | 323 | tpm2_flush_context(chip, blob_handle); |
2e19e101 | 324 | |
8c657a05 JS |
325 | out: |
326 | tpm_put_ops(chip); | |
327 | ||
2e19e101 SG |
328 | return rc; |
329 | } |