1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "host/commands/secure_env/primary_key_builder.h"
17 
18 #include <android-base/logging.h>
19 #include <tss2/tss2_mu.h>
20 #include <tss2/tss2_rc.h>
21 
22 namespace cuttlefish {
23 
PrimaryKeyBuilder()24 PrimaryKeyBuilder::PrimaryKeyBuilder() : public_area_({}) {
25   public_area_.nameAlg = TPM2_ALG_SHA256;
26 };
27 
SigningKey()28 void PrimaryKeyBuilder::SigningKey() {
29   public_area_.type = TPM2_ALG_KEYEDHASH;
30   public_area_.objectAttributes |= TPMA_OBJECT_SIGN_ENCRYPT;
31   public_area_.objectAttributes |= TPMA_OBJECT_USERWITHAUTH;
32   public_area_.objectAttributes |= TPMA_OBJECT_SENSITIVEDATAORIGIN;
33   public_area_.parameters.keyedHashDetail.scheme = (TPMT_KEYEDHASH_SCHEME) {
34     .scheme = TPM2_ALG_HMAC,
35     .details.hmac.hashAlg = TPM2_ALG_SHA256,
36   };
37 }
38 
ParentKey()39 void PrimaryKeyBuilder::ParentKey() {
40   public_area_.type = TPM2_ALG_SYMCIPHER;
41   public_area_.objectAttributes |= TPMA_OBJECT_USERWITHAUTH;
42   public_area_.objectAttributes |= TPMA_OBJECT_RESTRICTED;
43   public_area_.objectAttributes |= TPMA_OBJECT_DECRYPT;
44   public_area_.objectAttributes |= TPMA_OBJECT_FIXEDTPM;
45   public_area_.objectAttributes |= TPMA_OBJECT_FIXEDPARENT;
46   public_area_.objectAttributes |= TPMA_OBJECT_SENSITIVEDATAORIGIN;
47   public_area_.parameters.symDetail.sym = (TPMT_SYM_DEF_OBJECT) {
48     .algorithm = TPM2_ALG_AES,
49     .keyBits.aes = 128, // The default maximum AES key size in the simulator.
50     .mode.aes = TPM2_ALG_CFB,
51   };
52 }
53 
UniqueData(const std::string & data)54 void PrimaryKeyBuilder::UniqueData(const std::string& data) {
55   if (data.size() > TPM2_SHA256_DIGEST_SIZE) {
56     LOG(FATAL) << "Unique data size was too large";
57   }
58   /* The unique field normally has a precise size to go with the type of the
59    * object. During primary key creation the unique field accepts any short byte
60    * string to let the user introduce variability into the primary key creation
61    * process which is otherwise deterministic relative to secret TPM state. */
62   public_area_.unique.sym.size = data.size();
63   memcpy(&public_area_.unique.sym.buffer, data.data(), data.size());
64 }
65 
CreateKey(TpmResourceManager & resource_manager)66 TpmObjectSlot PrimaryKeyBuilder::CreateKey(
67     TpmResourceManager& resource_manager) {
68   TPM2B_AUTH authValue = {};
69   auto rc =
70       Esys_TR_SetAuth(*resource_manager.Esys(), ESYS_TR_RH_OWNER, &authValue);
71   if (rc != TSS2_RC_SUCCESS) {
72     LOG(ERROR) << "Esys_TR_SetAuth failed with return code " << rc
73                << " (" << Tss2_RC_Decode(rc) << ")";
74     return {};
75   }
76 
77   TPM2B_TEMPLATE public_template = {};
78   size_t offset = 0;
79   rc = Tss2_MU_TPMT_PUBLIC_Marshal(&public_area_, &public_template.buffer[0],
80                                    sizeof(public_template.buffer), &offset);
81   if (rc != TSS2_RC_SUCCESS) {
82     LOG(ERROR) << "Tss2_MU_TPMT_PUBLIC_Marshal failed with return code " << rc
83                << " (" << Tss2_RC_Decode(rc) << ")";
84     return {};
85   }
86   public_template.size = offset;
87 
88   TPM2B_SENSITIVE_CREATE in_sensitive = {};
89 
90   auto key_slot = resource_manager.ReserveSlot();
91   if (!key_slot) {
92     LOG(ERROR) << "No slots available";
93     return {};
94   }
95   ESYS_TR raw_handle;
96   // TODO(b/154956668): Define better ACLs on these keys.
97   // Since this is a primary key, it's generated deterministically. It would
98   // also be possible to generate this once and hold it in storage.
99   rc = Esys_CreateLoaded(
100       /* esysContext */ *resource_manager.Esys(),
101       /* primaryHandle */ ESYS_TR_RH_OWNER,
102       /* shandle1 */ ESYS_TR_PASSWORD,
103       /* shandle2 */ ESYS_TR_NONE,
104       /* shandle3 */ ESYS_TR_NONE,
105       /* inSensitive */ &in_sensitive,
106       /* inPublic */ &public_template,
107       /* objectHandle */ &raw_handle,
108       /* outPrivate */ nullptr,
109       /* outPublic */ nullptr);
110   if (rc != TSS2_RC_SUCCESS) {
111     LOG(ERROR) << "Esys_CreateLoaded failed with return code " << rc
112                << " (" << Tss2_RC_Decode(rc) << ")";
113     return {};
114   }
115   key_slot->set(raw_handle);
116   return key_slot;
117 }
118 
119 std::function<TpmObjectSlot(TpmResourceManager&)>
SigningKeyCreator(const std::string & unique)120 SigningKeyCreator(const std::string& unique) {
121   return [unique](TpmResourceManager& resource_manager) {
122     PrimaryKeyBuilder key_builder;
123     key_builder.SigningKey();
124     key_builder.UniqueData(unique);
125     return key_builder.CreateKey(resource_manager);
126   };
127 }
128 
129 std::function<TpmObjectSlot(TpmResourceManager&)>
ParentKeyCreator(const std::string & unique)130 ParentKeyCreator(const std::string& unique) {
131   return [unique](TpmResourceManager& resource_manager) {
132     PrimaryKeyBuilder key_builder;
133     key_builder.ParentKey();
134     key_builder.UniqueData(unique);
135     return key_builder.CreateKey(resource_manager);
136   };
137 }
138 
CreateSigningKey(TpmResourceManager & resource_manager,const std::string & unique_data)139 TpmObjectSlot PrimaryKeyBuilder::CreateSigningKey(
140     TpmResourceManager& resource_manager, const std::string& unique_data) {
141   return SigningKeyCreator(unique_data)(resource_manager);
142 }
143 
144 }  // namespace cuttlefish
145