Branch data Line data Source code
1 : : /* libsecret - TSS interface implementations for libsecret
2 : : *
3 : : * Copyright (C) 2021 Dhanuka Warusadura
4 : : *
5 : : * This library is free software; you can redistribute it and/or modify
6 : : * it under the terms of the GNU Lesser General Public License as
7 : : * published by the Free Software Foundation; either version 2.1 of
8 : : * the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful, but
11 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 : : * MA 02110-1301 USA
19 : : *
20 : : * Author: Dhanuka Warusadura
21 : : */
22 : :
23 : : #include "config.h"
24 : : #include "egg-tpm2.h"
25 : : #include <tss2/tss2_esys.h>
26 : : #include <tss2/tss2_mu.h>
27 : : #include <tss2/tss2_rc.h>
28 : : #include <tss2/tss2_tctildr.h>
29 : :
30 : : #define MAX_BYTE_SIZE 64
31 : :
32 : : struct EggTpm2Context {
33 : : TSS2_TCTI_CONTEXT *tcti_context;
34 : : ESYS_CONTEXT *esys_context;
35 : : ESYS_TR primary_key;
36 : : };
37 : :
38 : : static gboolean
39 : 9 : egg_tpm2_generate_primary_key(EggTpm2Context *context,
40 : : GError **error)
41 : : {
42 : : TSS2_RC ret;
43 : :
44 : 9 : TPM2B_SENSITIVE_CREATE sensitive_params = {
45 : : .size = 0,
46 : : .sensitive = {
47 : : .userAuth = {
48 : : .size = 0,
49 : : .buffer = {0},
50 : : },
51 : : .data = {
52 : : .size = 0,
53 : : .buffer = {0},
54 : : },
55 : : },
56 : : };
57 : :
58 : 9 : TPM2B_PUBLIC public_key_param = {
59 : : .size = 0,
60 : : .publicArea = {
61 : : .type = TPM2_ALG_RSA,
62 : : .nameAlg = TPM2_ALG_SHA256,
63 : : .objectAttributes = (
64 : : TPMA_OBJECT_USERWITHAUTH |
65 : : TPMA_OBJECT_RESTRICTED |
66 : : TPMA_OBJECT_DECRYPT |
67 : : TPMA_OBJECT_FIXEDTPM |
68 : : TPMA_OBJECT_FIXEDPARENT |
69 : : TPMA_OBJECT_SENSITIVEDATAORIGIN),
70 : : .authPolicy = {
71 : : .size = 0,
72 : : },
73 : : .parameters.rsaDetail = {
74 : : .symmetric = {
75 : : .algorithm = TPM2_ALG_AES,
76 : : .keyBits.aes = 128,
77 : : .mode.aes = TPM2_ALG_CFB
78 : : },
79 : : .scheme = {
80 : : .scheme = TPM2_ALG_NULL
81 : : },
82 : : .keyBits = 2048,
83 : : .exponent = 0,
84 : : },
85 : : .unique.rsa = {
86 : : .size = 0,
87 : : .buffer = {},
88 : : },
89 : : },
90 : : };
91 : :
92 : 9 : TPM2B_DATA outside_info = {
93 : : .size = 0,
94 : : .buffer = {},
95 : : };
96 : :
97 : 9 : TPML_PCR_SELECTION pcrs = {
98 : : .count = 0,
99 : : };
100 : :
101 : : TPM2B_PUBLIC *public;
102 : : TPM2B_CREATION_DATA *creation_data;
103 : : TPM2B_DIGEST *hash;
104 : : TPMT_TK_CREATION *ticket;
105 : :
106 : 9 : ret = Esys_CreatePrimary(context->esys_context, ESYS_TR_RH_OWNER,
107 : : ESYS_TR_PASSWORD, ESYS_TR_NONE,
108 : : ESYS_TR_NONE, &sensitive_params,
109 : : &public_key_param, &outside_info,
110 : : &pcrs, &context->primary_key, &public,
111 : : &creation_data, &hash, &ticket);
112 : :
113 [ - + ]: 9 : if (ret != TSS2_RC_SUCCESS) {
114 : 0 : g_set_error(error,
115 : : G_IO_ERROR,
116 : : G_IO_ERROR_FAILED,
117 : : "Esys_CreatePrimary: %s", Tss2_RC_Decode(ret));
118 : 0 : return FALSE;
119 : : }
120 : :
121 : 9 : Esys_Free(public);
122 : 9 : Esys_Free(creation_data);
123 : 9 : Esys_Free(hash);
124 : 9 : Esys_Free(ticket);
125 : :
126 : 9 : return TRUE;
127 : : }
128 : :
129 : : static GBytes *
130 : 3 : egg_tpm2_generate_random_data(EggTpm2Context *context,
131 : : GError **error)
132 : : {
133 : : TSS2_RC ret;
134 : : TPM2B_DIGEST *random_data;
135 : : GBytes *bytes;
136 : :
137 : 3 : ret = Esys_GetRandom(context->esys_context, ESYS_TR_NONE,
138 : : ESYS_TR_NONE, ESYS_TR_NONE, MAX_BYTE_SIZE,
139 : : &random_data);
140 : :
141 [ - + ]: 3 : if (ret != TSS2_RC_SUCCESS) {
142 : 0 : g_set_error(error,
143 : : G_IO_ERROR,
144 : : G_IO_ERROR_FAILED,
145 : : "Esys_GetRandom: %s", Tss2_RC_Decode(ret));
146 : 0 : return NULL;
147 : : }
148 : :
149 : 3 : bytes = g_bytes_new(random_data->buffer, random_data->size);
150 : 3 : Esys_Free(random_data);
151 : :
152 : 3 : return bytes;
153 : : }
154 : :
155 : : EggTpm2Context *
156 : 9 : egg_tpm2_initialize(GError **error)
157 : : {
158 : : TSS2_RC ret;
159 : : EggTpm2Context *context;
160 : : gsize n_context;
161 : : const gchar *tcti_conf;
162 : : gboolean status;
163 : :
164 : 9 : n_context = 1;
165 : 9 : context = g_new(EggTpm2Context, n_context);
166 : 9 : tcti_conf = g_getenv("TCTI");
167 : 9 : ret = Tss2_TctiLdr_Initialize(tcti_conf, &context->tcti_context);
168 : :
169 [ - + ]: 9 : if (ret != TSS2_RC_SUCCESS) {
170 : 0 : egg_tpm2_finalize(context);
171 : 0 : g_set_error(error,
172 : : G_IO_ERROR,
173 : : G_IO_ERROR_FAILED,
174 : : "Tss2_TctiLdr_Initialize: %s",
175 : : Tss2_RC_Decode(ret));
176 : 0 : return NULL;
177 : : }
178 : :
179 : 9 : ret = Esys_Initialize(&context->esys_context,
180 : : context->tcti_context, NULL);
181 [ - + ]: 9 : if (ret != TSS2_RC_SUCCESS) {
182 : 0 : egg_tpm2_finalize(context);
183 : 0 : g_set_error(error,
184 : : G_IO_ERROR,
185 : : G_IO_ERROR_FAILED,
186 : : "Esys_Initialize: %s", Tss2_RC_Decode(ret));
187 : 0 : return NULL;
188 : : }
189 : :
190 : 9 : ret = Esys_Startup(context->esys_context, TPM2_SU_CLEAR);
191 [ - + ]: 9 : if (ret != TSS2_RC_SUCCESS) {
192 : 0 : egg_tpm2_finalize(context);
193 : 0 : g_set_error(error,
194 : : G_IO_ERROR,
195 : : G_IO_ERROR_FAILED,
196 : : "Esys_Startup: %s", Tss2_RC_Decode(ret));
197 : 0 : return NULL;
198 : : }
199 : :
200 : 9 : status = egg_tpm2_generate_primary_key(context, error);
201 [ - + ]: 9 : if (!status) {
202 : 0 : egg_tpm2_finalize(context);
203 : 0 : return NULL;
204 : : }
205 : :
206 : 9 : return context;
207 : : }
208 : :
209 : : void
210 : 9 : egg_tpm2_finalize(EggTpm2Context *context)
211 : : {
212 [ + - ]: 9 : if (context->esys_context)
213 : 9 : Esys_Finalize(&context->esys_context);
214 : :
215 [ + - ]: 9 : if (context->tcti_context)
216 : 9 : Tss2_TctiLdr_Finalize(&context->tcti_context);
217 : :
218 : 9 : g_free(context);
219 : 9 : }
220 : :
221 : : GBytes *
222 : 3 : egg_tpm2_generate_master_password(EggTpm2Context *context,
223 : : GError **error)
224 : : {
225 : : TSS2_RC ret;
226 : : TPM2B_PRIVATE *out_private;
227 : : TPM2B_PUBLIC *out_public;
228 : : TPM2B_CREATION_DATA *creation_data;
229 : : TPM2B_DIGEST *hash;
230 : : TPMT_TK_CREATION *ticket;
231 : : gconstpointer data;
232 : : gsize size;
233 : : GBytes *input;
234 : : GBytes *output;
235 : :
236 : 3 : TPM2B_SENSITIVE_CREATE in_sensitive = {
237 : : .size = 0,
238 : : .sensitive = {
239 : : .data = {
240 : : .size = MAX_BYTE_SIZE
241 : : }
242 : : }
243 : : };
244 : :
245 : 3 : TPM2B_PUBLIC in_public = {
246 : : .size = 0,
247 : : .publicArea = {
248 : : .type = TPM2_ALG_KEYEDHASH,
249 : : .nameAlg = TPM2_ALG_SHA256,
250 : : .objectAttributes = (
251 : : TPMA_OBJECT_USERWITHAUTH |
252 : : TPMA_OBJECT_FIXEDTPM |
253 : : TPMA_OBJECT_FIXEDPARENT),
254 : : .authPolicy = {
255 : : .size = 0,
256 : : },
257 : : .parameters.keyedHashDetail = {
258 : : .scheme = {
259 : : .scheme = TPM2_ALG_NULL,
260 : : .details = {
261 : : .hmac = {
262 : : .hashAlg =
263 : : TPM2_ALG_SHA256
264 : : }
265 : : }
266 : : }
267 : : },
268 : : .unique.keyedHash = {
269 : : .size = 0,
270 : : .buffer = {},
271 : : },
272 : : }
273 : : };
274 : :
275 : 3 : TPM2B_DATA outside_info = {
276 : : .size = 0,
277 : : .buffer = {}
278 : : };
279 : :
280 : 3 : TPML_PCR_SELECTION pcrs = {
281 : : .count = 0
282 : : };
283 : :
284 : 3 : input = egg_tpm2_generate_random_data(context, error);
285 [ - + ]: 3 : if (!input) {
286 : 0 : g_bytes_unref(input);
287 : 0 : return NULL;
288 : : }
289 : :
290 : 3 : data = g_bytes_get_data(input, &size);
291 [ - + ]: 3 : if (size > sizeof(in_sensitive.sensitive.data.buffer)) {
292 : 0 : g_set_error_literal(error,
293 : : G_IO_ERROR,
294 : : G_IO_ERROR_INVALID_ARGUMENT,
295 : : "Input is too long");
296 : 0 : return NULL;
297 : : }
298 : :
299 : 3 : memcpy(in_sensitive.sensitive.data.buffer, data, size);
300 : 3 : in_sensitive.sensitive.data.size = size;
301 : 3 : g_bytes_unref(input);
302 : :
303 : 3 : ret = Esys_Create(context->esys_context, context->primary_key,
304 : : ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
305 : : &in_sensitive, &in_public, &outside_info,
306 : : &pcrs, &out_private, &out_public, &creation_data,
307 : : &hash, &ticket);
308 [ - + ]: 3 : if (ret != TSS2_RC_SUCCESS) {
309 : 0 : g_set_error(error,
310 : : G_IO_ERROR,
311 : : G_IO_ERROR_FAILED,
312 : : "Esys_Create: %s", Tss2_RC_Decode(ret));
313 : 0 : return NULL;
314 : : }
315 : :
316 : 3 : gsize out_private_offset = 0;
317 : 3 : gsize out_public_offset = 0;
318 : : GVariant *out_private_variant;
319 : : GVariant *out_public_variant;
320 : : GVariant *variant;
321 : :
322 : : guint8 marshaled_out_private[sizeof(*out_private)];
323 : : guint8 marshaled_out_public[sizeof(*out_public)];
324 : :
325 : 3 : ret = Tss2_MU_TPM2B_PRIVATE_Marshal(out_private,
326 : : marshaled_out_private,
327 : : sizeof(marshaled_out_private),
328 : : &out_private_offset);
329 [ - + ]: 3 : if (ret != TSS2_RC_SUCCESS) {
330 : 0 : g_set_error(error,
331 : : G_IO_ERROR,
332 : : G_IO_ERROR_FAILED,
333 : : "Tss2_MU_TPM2B_PRIVATE_Marshal: %s",
334 : : Tss2_RC_Decode(ret));
335 : 0 : return NULL;
336 : : }
337 : :
338 : 3 : out_private_variant = g_variant_new_fixed_array(
339 : : G_VARIANT_TYPE_BYTE,
340 : : marshaled_out_private,
341 : : out_private_offset,
342 : : sizeof(guint8));
343 : :
344 : 3 : ret = Tss2_MU_TPM2B_PUBLIC_Marshal(out_public,
345 : : marshaled_out_public,
346 : : sizeof(marshaled_out_public),
347 : : &out_public_offset);
348 [ - + ]: 3 : if (ret != TSS2_RC_SUCCESS) {
349 : 0 : g_set_error(error,
350 : : G_IO_ERROR,
351 : : G_IO_ERROR_FAILED,
352 : : "Tss2_MU_TPM2B_PUBLIC_Marshal: %s",
353 : : Tss2_RC_Decode(ret));
354 : 0 : return NULL;
355 : : }
356 : :
357 : 3 : out_public_variant = g_variant_new_fixed_array(
358 : : G_VARIANT_TYPE_BYTE,
359 : : marshaled_out_public,
360 : : out_public_offset,
361 : : sizeof(guint8));
362 : :
363 : 3 : variant = g_variant_new("(u@ayu@ay)",
364 : : out_private_offset, out_private_variant,
365 : : out_public_offset, out_public_variant);
366 : :
367 : 3 : output = g_variant_get_data_as_bytes(variant);
368 : :
369 : 3 : g_variant_unref(variant);
370 : 3 : Esys_Free(out_public);
371 : 3 : Esys_Free(out_private);
372 : 3 : Esys_Free(creation_data);
373 : 3 : Esys_Free(hash);
374 : 3 : Esys_Free(ticket);
375 : :
376 : 3 : return output;
377 : : }
378 : :
379 : : GBytes *
380 : 8 : egg_tpm2_decrypt_master_password(EggTpm2Context *context,
381 : : GBytes *input,
382 : : GError **error)
383 : : {
384 : : TSS2_RC ret;
385 : : GBytes *output;
386 : : TPM2B_SENSITIVE_DATA *out_data;
387 : : GVariant *variant;
388 : : gconstpointer data;
389 : 8 : gsize out_private_offset = 0;
390 : 8 : gsize out_public_offset = 0;
391 : 8 : gsize count = 0;
392 : 8 : gsize offset = 0;
393 : : GVariant *out_private_variant;
394 : : GVariant *out_public_variant;
395 : : ESYS_TR out_key;
396 : :
397 : 8 : variant = g_variant_new_from_bytes(G_VARIANT_TYPE(
398 : : "(uayuay)"),
399 : : input,
400 : : TRUE);
401 : :
402 : 8 : g_variant_get(variant, "(u@ayu@ay)",
403 : : &out_private_offset, &out_private_variant,
404 : : &out_public_offset, &out_public_variant);
405 : 8 : g_variant_unref(variant);
406 : :
407 : 8 : data = g_variant_get_fixed_array(out_private_variant,
408 : : &count,
409 : : sizeof(guint8));
410 : 8 : guint8 *marshaled_out_private = g_memdup2(data, count);
411 : 8 : count = 0;
412 : :
413 : 8 : TPM2B_PRIVATE out_private = {
414 : : .size = 0
415 : : };
416 : 8 : ret = Tss2_MU_TPM2B_PRIVATE_Unmarshal(marshaled_out_private,
417 : : out_private_offset,
418 : : &offset,
419 : : &out_private);
420 : :
421 : 8 : g_variant_unref(out_private_variant);
422 : 8 : g_free(marshaled_out_private);
423 : :
424 [ - + ]: 8 : if (ret != TSS2_RC_SUCCESS) {
425 : 0 : g_set_error(error,
426 : : G_IO_ERROR,
427 : : G_IO_ERROR_FAILED,
428 : : "Tss2_MU_TPM2B_PRIVATE_Unmarshal: %s",
429 : : Tss2_RC_Decode(ret));
430 : 0 : return NULL;
431 : : }
432 : :
433 : 8 : offset = 0;
434 : 8 : data = g_variant_get_fixed_array(out_public_variant,
435 : : &count,
436 : : sizeof(guint8));
437 : 8 : guint8 *marshaled_out_public = g_memdup2(data, count);
438 : :
439 : 8 : TPM2B_PUBLIC out_public = {
440 : : .size = 0
441 : : };
442 : 8 : ret = Tss2_MU_TPM2B_PUBLIC_Unmarshal(marshaled_out_public,
443 : : out_public_offset,
444 : : &offset,
445 : : &out_public);
446 : :
447 : 8 : g_variant_unref(out_public_variant);
448 : 8 : g_free(marshaled_out_public);
449 : :
450 [ - + ]: 8 : if (ret != TSS2_RC_SUCCESS) {
451 : 0 : g_set_error(error,
452 : : G_IO_ERROR,
453 : : G_IO_ERROR_FAILED,
454 : : "Tss2_MU_TPM2B_PUBLIC_Unmarshal: %s",
455 : : Tss2_RC_Decode(ret));
456 : 0 : return NULL;
457 : : }
458 : :
459 : 8 : ret = Esys_Load(context->esys_context, context->primary_key,
460 : : ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
461 : : &out_private, &out_public, &out_key);
462 [ - + ]: 8 : if (ret != TSS2_RC_SUCCESS) {
463 : 0 : g_set_error(error,
464 : : G_IO_ERROR,
465 : : G_IO_ERROR_FAILED,
466 : : "Esys_Load: %s", Tss2_RC_Decode(ret));
467 : 0 : return NULL;
468 : : }
469 : :
470 : 8 : ret = Esys_Unseal(context->esys_context, out_key,
471 : : ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
472 : : &out_data);
473 [ - + ]: 8 : if (ret != TSS2_RC_SUCCESS) {
474 : 0 : g_set_error(error,
475 : : G_IO_ERROR,
476 : : G_IO_ERROR_FAILED,
477 : : "Esys_Unseal: %s", Tss2_RC_Decode(ret));
478 : 0 : return NULL;
479 : : }
480 : :
481 : 8 : output = g_bytes_new(out_data->buffer, out_data->size);
482 : 8 : Esys_Free(out_data);
483 : :
484 : 8 : return output;
485 : : }
|