Some changes required for the actual integration with a GUI renderer
These changes accumulated during the integration with the Pixel specific
impelemtation. The make it easiser to integrate an GUI renderer with the
core logic.
Bug: 63928580
Test: VTS tests and manual tests
Change-Id: I7001f60709ce806a16f098492bdb71eb05e6ca9a
diff --git a/confirmationui/1.0/default/ConfirmationUI.cpp b/confirmationui/1.0/default/ConfirmationUI.cpp
index f241a76..41e03ce 100644
--- a/confirmationui/1.0/default/ConfirmationUI.cpp
+++ b/confirmationui/1.0/default/ConfirmationUI.cpp
@@ -43,7 +43,12 @@
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(
diff --git a/confirmationui/1.0/default/PlatformSpecifics.h b/confirmationui/1.0/default/PlatformSpecifics.h
index 18b88c8..488da6d 100644
--- a/confirmationui/1.0/default/PlatformSpecifics.h
+++ b/confirmationui/1.0/default/PlatformSpecifics.h
@@ -52,8 +52,14 @@
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
diff --git a/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h b/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h
index a88cd40..b480942 100644
--- a/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h
+++ b/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h
@@ -55,10 +55,27 @@
(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;
@@ -71,20 +88,20 @@
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;
@@ -92,7 +109,6 @@
}
void userCancel() {
- // tear down TUI here
if (isPending()) error_ = ResponseCode::Canceled;
}
@@ -104,10 +120,10 @@
}
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) {
@@ -156,7 +172,6 @@
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()) {
@@ -169,9 +184,10 @@
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_;