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 "tpm_hmac.h"
17 
18 #include <android-base/logging.h>
19 #include <tss2/tss2_esys.h>
20 #include <tss2/tss2_rc.h>
21 
22 #include "host/commands/secure_env/primary_key_builder.h"
23 #include "host/commands/secure_env/tpm_resource_manager.h"
24 
25 namespace cuttlefish {
26 
27 /* For data large enough to fit in a single TPM2_HMAC call. */
OneshotHmac(TpmResourceManager & resource_manager,ESYS_TR key_handle,TpmAuth auth,const uint8_t * data,size_t data_size)28 static UniqueEsysPtr<TPM2B_DIGEST> OneshotHmac(
29     TpmResourceManager& resource_manager,
30     ESYS_TR key_handle,
31     TpmAuth auth,
32     const uint8_t* data,
33     size_t data_size) {
34   if (data_size  > TPM2_MAX_DIGEST_BUFFER) {
35     LOG(ERROR) << "Logic error: OneshotSign called with data_size "
36                << data_size << " (> " << TPM2_MAX_DIGEST_BUFFER << ")";
37     return {};
38   }
39   TPM2B_MAX_BUFFER buffer;
40   static_assert(sizeof(buffer.buffer) >= TPM2_MAX_DIGEST_BUFFER);
41   buffer.size = data_size;
42   memcpy(buffer.buffer, data, data_size);
43   TPM2B_DIGEST* out_hmac = nullptr;
44   auto rc =
45       Esys_HMAC(*resource_manager.Esys(), key_handle, auth.auth1(),
46                 auth.auth2(), auth.auth3(), &buffer, TPM2_ALG_NULL, &out_hmac);
47   if (rc != TPM2_RC_SUCCESS) {
48     LOG(ERROR) << "TPM2_HMAC failed: " << Tss2_RC_Decode(rc) << "(" << rc << ")";
49     return {};
50   }
51   if (out_hmac == nullptr) {
52     LOG(ERROR) << "out_hmac unset";
53     return {};
54   }
55   return UniqueEsysPtr<TPM2B_DIGEST>(out_hmac);
56 }
57 
58 /* For data too large to fit in a single TPM2_HMAC call. */
SegmentedHmac(TpmResourceManager & resource_manager,ESYS_TR key_handle,TpmAuth key_auth,const uint8_t * data,size_t data_size)59 static UniqueEsysPtr<TPM2B_DIGEST> SegmentedHmac(
60     TpmResourceManager& resource_manager,
61     ESYS_TR key_handle,
62     TpmAuth key_auth,
63     const uint8_t* data,
64     size_t data_size) {
65   // TODO(schuffelen): Pipeline commands where possible.
66   TPM2B_AUTH sequence_auth;
67   sequence_auth.size = sizeof(rand());
68   *reinterpret_cast<decltype(rand())*>(sequence_auth.buffer) = rand();
69   ESYS_TR sequence_handle;
70   auto slot = resource_manager.ReserveSlot();
71   if (!slot) {
72     LOG(ERROR) << "No slots available";
73     return {};
74   }
75   auto locked_esys = resource_manager.Esys();
76   auto rc = Esys_HMAC_Start(*locked_esys, key_handle, key_auth.auth1(),
77                             key_auth.auth2(), key_auth.auth3(), &sequence_auth,
78                             TPM2_ALG_NULL, &sequence_handle);
79   if (rc != TPM2_RC_SUCCESS) {
80     LOG(ERROR) << "TPM2_HMAC_Start failed: " << Tss2_RC_Decode(rc)
81                << "(" << rc << ")";
82     return {};
83   }
84   slot->set(sequence_handle);
85   rc = Esys_TR_SetAuth(*locked_esys, sequence_handle,
86                        &sequence_auth);
87   if (rc != TPM2_RC_SUCCESS) {
88     LOG(ERROR) << "Esys_TR_SetAuth failed: " << Tss2_RC_Decode(rc)
89                << "(" << rc << ")";
90     return {};
91   }
92   auto hashed = 0;
93   TPM2B_MAX_BUFFER buffer;
94   while (data_size - hashed > TPM2_MAX_DIGEST_BUFFER) {
95     buffer.size = TPM2_MAX_DIGEST_BUFFER;
96     memcpy(buffer.buffer, &data[hashed], TPM2_MAX_DIGEST_BUFFER);
97     hashed += TPM2_MAX_DIGEST_BUFFER;
98     rc = Esys_SequenceUpdate(*locked_esys, sequence_handle, ESYS_TR_PASSWORD,
99                              ESYS_TR_NONE, ESYS_TR_NONE, &buffer);
100     if (rc != TPM2_RC_SUCCESS) {
101       LOG(ERROR) << "Esys_SequenceUpdate failed: " << Tss2_RC_Decode(rc)
102                 << "(" << rc << ")";
103       return {};
104     }
105   }
106   buffer.size = data_size - hashed;
107   memcpy(buffer.buffer, &data[hashed], buffer.size);
108   TPM2B_DIGEST* out_hmac = nullptr;
109   TPMT_TK_HASHCHECK* validation = nullptr;
110   rc = Esys_SequenceComplete(*locked_esys, sequence_handle, ESYS_TR_PASSWORD,
111                              ESYS_TR_NONE, ESYS_TR_NONE, &buffer, TPM2_RH_OWNER,
112                              &out_hmac, &validation);
113   if (rc != TPM2_RC_SUCCESS) {
114     LOG(ERROR) << "Esys_SequenceComplete failed: " << Tss2_RC_Decode(rc)
115                << "(" << rc << ")";
116     return {};
117   }
118   // TPM2_SequenceComplete already flushes the sequence context on success.
119   slot->set(ESYS_TR_NONE);
120   if (out_hmac == nullptr) {
121     LOG(ERROR) << "out_hmac was null";
122     return {};
123   }
124   Esys_Free(validation);
125   return UniqueEsysPtr<TPM2B_DIGEST>(out_hmac);
126 }
127 
TpmHmac(TpmResourceManager & resource_manager,ESYS_TR key_handle,TpmAuth auth,const uint8_t * data,size_t data_size)128 UniqueEsysPtr<TPM2B_DIGEST> TpmHmac(
129     TpmResourceManager& resource_manager,
130     ESYS_TR key_handle,
131     TpmAuth auth,
132     const uint8_t* data,
133     size_t data_size) {
134   auto fn = data_size > TPM2_MAX_DIGEST_BUFFER ? SegmentedHmac : OneshotHmac;
135 
136   return fn(resource_manager, key_handle, auth, data, data_size);
137 }
138 
TpmHmacWithContext(TpmResourceManager & resource_manager,const std::string & context,const uint8_t * data,size_t data_size)139 UniqueEsysPtr<TPM2B_DIGEST> TpmHmacWithContext(
140     TpmResourceManager& resource_manager, const std::string& context,
141     const uint8_t* data, size_t data_size) {
142   // Use the same context for all HMAC operations (ignoring the provided
143   // `context` parameter) to better comply with the KeyMint spec.
144   auto key_slot =
145       PrimaryKeyBuilder::CreateSigningKey(resource_manager, "TpmHmac_context");
146   if (!key_slot) {
147     LOG(ERROR) << "Could not make signing key for " << context;
148     return nullptr;
149   }
150   return TpmHmac(resource_manager, key_slot->get(), TpmAuth(ESYS_TR_PASSWORD),
151                  data, data_size);
152 }
153 
154 }  // namespace cuttlefish
155