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