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_;