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