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