1 /*
2 **
3 ** Copyright 2017, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #ifndef CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
19 #define CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
20
21 #include <android/hardware/confirmationui/1.0/types.h>
22 #include <android/hardware/confirmationui/support/cbor.h>
23 #include <android/hardware/confirmationui/support/confirmationui_utils.h>
24 #include <android/hardware/keymaster/4.0/types.h>
25
26 namespace android {
27 namespace hardware {
28 namespace confirmationui {
29 namespace V1_0 {
30 namespace generic {
31
32 namespace {
33 using namespace ::android::hardware::confirmationui::support;
34 using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
35 using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
36
hasOption(UIOption option,const hidl_vec<UIOption> & uiOptions)37 inline bool hasOption(UIOption option, const hidl_vec<UIOption>& uiOptions) {
38 for (auto& o : uiOptions) {
39 if (o == option) return true;
40 }
41 return false;
42 }
43
44 template <typename Callback, typename TimeStamper, typename HmacImplementation>
45 class Operation {
46 using HMacer = support::HMac<HmacImplementation>;
47
48 public:
Operation()49 Operation() : error_(ResponseCode::Ignored), formattedMessageLength_(0) {}
50
init(const Callback & resultCB,const hidl_string & promptText,const hidl_vec<uint8_t> & extraData,const hidl_string & locale,const hidl_vec<UIOption> & uiOptions)51 ResponseCode init(const Callback& resultCB, const hidl_string& promptText,
52 const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
53 const hidl_vec<UIOption>& uiOptions) {
54 (void)locale;
55 (void)uiOptions;
56 resultCB_ = resultCB;
57 if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending;
58
59 // We need to access the prompt text multiple times. Once for formatting the CBOR message
60 // and again for rendering the dialog. It is vital that the prompt does not change
61 // in the meantime. As of this point the prompt text is in a shared buffer and therefore
62 // susceptible to TOCTOU attacks. Note that promptText.size() resides on the stack and
63 // is safe to access multiple times. So now we copy the prompt string into the
64 // scratchpad promptStringBuffer_ from where we can format the CBOR message and then
65 // pass it to the renderer.
66 if (promptText.size() >= uint32_t(MessageSize::MAX))
67 return ResponseCode::UIErrorMessageTooLong;
68 auto pos = std::copy(promptText.c_str(), promptText.c_str() + promptText.size(),
69 promptStringBuffer_);
70 *pos = 0; // null-terminate the prompt for the renderer.
71
72 // Note the extra data is accessed only once for formating the CBOR message. So it is safe
73 // to read it from the shared buffer directly. Anyway we don't trust or interpret the
74 // extra data in any way so all we do is take a snapshot and we don't care if it is
75 // modified concurrently.
76 auto state = write(WriteState(formattedMessageBuffer_),
77 map(pair(text("prompt"), text(promptStringBuffer_, promptText.size())),
78 pair(text("extra"), bytes(extraData))));
79 switch (state.error_) {
80 case Error::OK:
81 break;
82 case Error::OUT_OF_DATA:
83 return ResponseCode::UIErrorMessageTooLong;
84 case Error::MALFORMED_UTF8:
85 return ResponseCode::UIErrorMalformedUTF8Encoding;
86 case Error::MALFORMED:
87 default:
88 return ResponseCode::Unexpected;
89 }
90 formattedMessageLength_ = state.data_ - formattedMessageBuffer_;
91
92 // on success record the start time
93 startTime_ = TimeStamper::now();
94 if (!startTime_.isOk()) {
95 return ResponseCode::SystemError;
96 }
97 return ResponseCode::OK;
98 }
99
setPending()100 void setPending() { error_ = ResponseCode::OK; }
101
setHmacKey(const auth_token_key_t & key)102 void setHmacKey(const auth_token_key_t& key) { hmacKey_ = key; }
hmacKey()103 NullOr<auth_token_key_t> hmacKey() const { return hmacKey_; }
104
abort()105 void abort() {
106 if (isPending()) {
107 resultCB_->result(ResponseCode::Aborted, {}, {});
108 error_ = ResponseCode::Ignored;
109 }
110 }
111
userCancel()112 void userCancel() {
113 if (isPending()) error_ = ResponseCode::Canceled;
114 }
115
finalize(const auth_token_key_t & key)116 void finalize(const auth_token_key_t& key) {
117 if (error_ == ResponseCode::Ignored) return;
118 resultCB_->result(error_, getMessage(), userConfirm(key));
119 error_ = ResponseCode::Ignored;
120 resultCB_ = {};
121 }
122
isPending()123 bool isPending() const { return error_ != ResponseCode::Ignored; }
getPrompt()124 const hidl_string getPrompt() const {
125 hidl_string s;
126 s.setToExternal(promptStringBuffer_, strlen(promptStringBuffer_));
127 return s;
128 }
129
deliverSecureInputEvent(const HardwareAuthToken & secureInputToken)130 ResponseCode deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
131 const auth_token_key_t testKey(static_cast<uint8_t>(TestKeyBits::BYTE));
132
133 auto hmac = HMacer::hmac256(testKey, "\0", bytes_cast(secureInputToken.challenge),
134 bytes_cast(secureInputToken.userId),
135 bytes_cast(secureInputToken.authenticatorId),
136 bytes_cast(hton(secureInputToken.authenticatorType)),
137 bytes_cast(hton(secureInputToken.timestamp)));
138 if (!hmac.isOk()) return ResponseCode::Unexpected;
139 if (hmac.value() == secureInputToken.mac) {
140 // okay so this is a test token
141 switch (static_cast<TestModeCommands>(secureInputToken.challenge)) {
142 case TestModeCommands::OK_EVENT: {
143 if (isPending()) {
144 finalize(testKey);
145 return ResponseCode::OK;
146 } else {
147 return ResponseCode::Ignored;
148 }
149 }
150 case TestModeCommands::CANCEL_EVENT: {
151 bool ignored = !isPending();
152 userCancel();
153 finalize(testKey);
154 return ignored ? ResponseCode::Ignored : ResponseCode::OK;
155 }
156 default:
157 return ResponseCode::Ignored;
158 }
159 }
160 return ResponseCode::Ignored;
161 }
162
163 private:
acceptAuthToken(const HardwareAuthToken &)164 bool acceptAuthToken(const HardwareAuthToken&) { return false; }
getMessage()165 hidl_vec<uint8_t> getMessage() {
166 hidl_vec<uint8_t> result;
167 if (error_ != ResponseCode::OK) return {};
168 result.setToExternal(formattedMessageBuffer_, formattedMessageLength_);
169 return result;
170 }
userConfirm(const auth_token_key_t & key)171 hidl_vec<uint8_t> userConfirm(const auth_token_key_t& key) {
172 if (error_ != ResponseCode::OK) return {};
173 confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage());
174 if (!confirmationTokenScratchpad_.isOk()) {
175 error_ = ResponseCode::Unexpected;
176 return {};
177 }
178 hidl_vec<uint8_t> result;
179 result.setToExternal(confirmationTokenScratchpad_->data(),
180 confirmationTokenScratchpad_->size());
181 return result;
182 }
183
184 ResponseCode error_ = ResponseCode::Ignored;
185 uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)];
186 char promptStringBuffer_[uint32_t(MessageSize::MAX)];
187 size_t formattedMessageLength_ = 0;
188 NullOr<hmac_t> confirmationTokenScratchpad_;
189 Callback resultCB_;
190 typename TimeStamper::TimeStamp startTime_;
191 NullOr<auth_token_key_t> hmacKey_;
192 };
193
194 } // namespace
195 } // namespace generic
196 } // namespace V1_0
197 } // namespace confirmationui
198 } // namespace hardware
199 } // namespace android
200
201 #endif // CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
202