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