Loading confirmationui/1.0/default/ConfirmationUI.cpp +6 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,12 @@ Return<ResponseCode> ConfirmationUI::promptUserConfirmation( const hidl_vec<uint8_t>& extraData, const hidl_string& locale, const hidl_vec<UIOption>& uiOptions) { auto& operation = MyOperation::get(); return operation.init(resultCB, promptText, extraData, locale, uiOptions); auto result = operation.init(resultCB, promptText, extraData, locale, uiOptions); if (result == ResponseCode::OK) { // This is where implementation start the UI and then call setPending on success. operation.setPending(); } return result; } Return<ResponseCode> ConfirmationUI::deliverSecureInputEvent( Loading confirmationui/1.0/default/PlatformSpecifics.h +8 −2 Original line number Diff line number Diff line Loading @@ -52,8 +52,14 @@ class HMacImplementation { const uint8_t key[32], std::initializer_list<support::ByteBufferProxy> buffers); }; using MyOperation = generic::Operation<sp<IConfirmationResultCallback>, MonotonicClockTimeStamper, HMacImplementation>; class MyOperation : public generic::Operation<sp<IConfirmationResultCallback>, MonotonicClockTimeStamper, HMacImplementation> { public: static MyOperation& get() { static MyOperation op; return op; } }; } // namespace implementation } // namespace V1_0 Loading confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h +31 −15 Original line number Diff line number Diff line Loading @@ -55,10 +55,27 @@ class Operation { (void)uiOptions; resultCB_ = resultCB; if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending; // TODO make copy of promptText before using it may reside in shared buffer auto state = write( WriteState(formattedMessageBuffer_), map(pair(text("prompt"), text(promptText)), pair(text("extra"), bytes(extraData)))); // We need to access the prompt text multiple times. Once for formatting the CBOR message // and again for rendering the dialog. It is vital that the prompt does not change // in the meantime. As of this point the prompt text is in a shared buffer and therefore // susceptible to TOCTOU attacks. Note that promptText.size() resides on the stack and // is safe to access multiple times. So now we copy the prompt string into the // scratchpad promptStringBuffer_ from where we can format the CBOR message and then // pass it to the renderer. if (promptText.size() >= uint32_t(MessageSize::MAX)) return ResponseCode::UIErrorMessageTooLong; auto pos = std::copy(promptText.c_str(), promptText.c_str() + promptText.size(), promptStringBuffer_); *pos = 0; // null-terminate the prompt for the renderer. // Note the extra data is accessed only once for formating the CBOR message. So it is safe // to read it from the shared buffer directly. Anyway we don't trust or interpret the // extra data in any way so all we do is take a snapshot and we don't care if it is // modified concurrently. auto state = write(WriteState(formattedMessageBuffer_), map(pair(text("prompt"), text(promptStringBuffer_, promptText.size())), pair(text("extra"), bytes(extraData)))); switch (state.error_) { case Error::OK: break; Loading @@ -71,20 +88,20 @@ class Operation { return ResponseCode::Unexpected; } formattedMessageLength_ = state.data_ - formattedMessageBuffer_; // setup TUI and diagnose more UI errors here. // on success record the start time startTime_ = TimeStamper::now(); if (!startTime_.isOk()) { return ResponseCode::SystemError; } error_ = ResponseCode::OK; return ResponseCode::OK; } void setPending() { error_ = ResponseCode::OK; } void setHmacKey(const uint8_t (&key)[32]) { hmacKey_ = {key}; } void abort() { // tear down TUI here if (isPending()) { resultCB_->result(ResponseCode::Aborted, {}, {}); error_ = ResponseCode::Ignored; Loading @@ -92,7 +109,6 @@ class Operation { } void userCancel() { // tear down TUI here if (isPending()) error_ = ResponseCode::Canceled; } Loading @@ -104,10 +120,10 @@ class Operation { } bool isPending() const { return error_ != ResponseCode::Ignored; } static Operation& get() { static Operation operation; return operation; const hidl_string getPrompt() const { hidl_string s; s.setToExternal(promptStringBuffer_, strlen(promptStringBuffer_)); return s; } ResponseCode deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) { Loading Loading @@ -156,7 +172,6 @@ class Operation { return result; } hidl_vec<uint8_t> userConfirm(const uint8_t key[32]) { // tear down TUI here if (error_ != ResponseCode::OK) return {}; confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage()); if (!confirmationTokenScratchpad_.isOk()) { Loading @@ -169,9 +184,10 @@ class Operation { return result; } ResponseCode error_; ResponseCode error_ = ResponseCode::Ignored; uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)]; size_t formattedMessageLength_; char promptStringBuffer_[uint32_t(MessageSize::MAX)]; size_t formattedMessageLength_ = 0; NullOr<array<uint8_t, 32>> confirmationTokenScratchpad_; Callback resultCB_; typename TimeStamper::TimeStamp startTime_; Loading Loading
confirmationui/1.0/default/ConfirmationUI.cpp +6 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,12 @@ Return<ResponseCode> ConfirmationUI::promptUserConfirmation( const hidl_vec<uint8_t>& extraData, const hidl_string& locale, const hidl_vec<UIOption>& uiOptions) { auto& operation = MyOperation::get(); return operation.init(resultCB, promptText, extraData, locale, uiOptions); auto result = operation.init(resultCB, promptText, extraData, locale, uiOptions); if (result == ResponseCode::OK) { // This is where implementation start the UI and then call setPending on success. operation.setPending(); } return result; } Return<ResponseCode> ConfirmationUI::deliverSecureInputEvent( Loading
confirmationui/1.0/default/PlatformSpecifics.h +8 −2 Original line number Diff line number Diff line Loading @@ -52,8 +52,14 @@ class HMacImplementation { const uint8_t key[32], std::initializer_list<support::ByteBufferProxy> buffers); }; using MyOperation = generic::Operation<sp<IConfirmationResultCallback>, MonotonicClockTimeStamper, HMacImplementation>; class MyOperation : public generic::Operation<sp<IConfirmationResultCallback>, MonotonicClockTimeStamper, HMacImplementation> { public: static MyOperation& get() { static MyOperation op; return op; } }; } // namespace implementation } // namespace V1_0 Loading
confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h +31 −15 Original line number Diff line number Diff line Loading @@ -55,10 +55,27 @@ class Operation { (void)uiOptions; resultCB_ = resultCB; if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending; // TODO make copy of promptText before using it may reside in shared buffer auto state = write( WriteState(formattedMessageBuffer_), map(pair(text("prompt"), text(promptText)), pair(text("extra"), bytes(extraData)))); // We need to access the prompt text multiple times. Once for formatting the CBOR message // and again for rendering the dialog. It is vital that the prompt does not change // in the meantime. As of this point the prompt text is in a shared buffer and therefore // susceptible to TOCTOU attacks. Note that promptText.size() resides on the stack and // is safe to access multiple times. So now we copy the prompt string into the // scratchpad promptStringBuffer_ from where we can format the CBOR message and then // pass it to the renderer. if (promptText.size() >= uint32_t(MessageSize::MAX)) return ResponseCode::UIErrorMessageTooLong; auto pos = std::copy(promptText.c_str(), promptText.c_str() + promptText.size(), promptStringBuffer_); *pos = 0; // null-terminate the prompt for the renderer. // Note the extra data is accessed only once for formating the CBOR message. So it is safe // to read it from the shared buffer directly. Anyway we don't trust or interpret the // extra data in any way so all we do is take a snapshot and we don't care if it is // modified concurrently. auto state = write(WriteState(formattedMessageBuffer_), map(pair(text("prompt"), text(promptStringBuffer_, promptText.size())), pair(text("extra"), bytes(extraData)))); switch (state.error_) { case Error::OK: break; Loading @@ -71,20 +88,20 @@ class Operation { return ResponseCode::Unexpected; } formattedMessageLength_ = state.data_ - formattedMessageBuffer_; // setup TUI and diagnose more UI errors here. // on success record the start time startTime_ = TimeStamper::now(); if (!startTime_.isOk()) { return ResponseCode::SystemError; } error_ = ResponseCode::OK; return ResponseCode::OK; } void setPending() { error_ = ResponseCode::OK; } void setHmacKey(const uint8_t (&key)[32]) { hmacKey_ = {key}; } void abort() { // tear down TUI here if (isPending()) { resultCB_->result(ResponseCode::Aborted, {}, {}); error_ = ResponseCode::Ignored; Loading @@ -92,7 +109,6 @@ class Operation { } void userCancel() { // tear down TUI here if (isPending()) error_ = ResponseCode::Canceled; } Loading @@ -104,10 +120,10 @@ class Operation { } bool isPending() const { return error_ != ResponseCode::Ignored; } static Operation& get() { static Operation operation; return operation; const hidl_string getPrompt() const { hidl_string s; s.setToExternal(promptStringBuffer_, strlen(promptStringBuffer_)); return s; } ResponseCode deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) { Loading Loading @@ -156,7 +172,6 @@ class Operation { return result; } hidl_vec<uint8_t> userConfirm(const uint8_t key[32]) { // tear down TUI here if (error_ != ResponseCode::OK) return {}; confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage()); if (!confirmationTokenScratchpad_.isOk()) { Loading @@ -169,9 +184,10 @@ class Operation { return result; } ResponseCode error_; ResponseCode error_ = ResponseCode::Ignored; uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)]; size_t formattedMessageLength_; char promptStringBuffer_[uint32_t(MessageSize::MAX)]; size_t formattedMessageLength_ = 0; NullOr<array<uint8_t, 32>> confirmationTokenScratchpad_; Callback resultCB_; typename TimeStamper::TimeStamp startTime_; Loading