Moved confirmationui support library to libteeui
This patch moves functionality from the support library in
hardware/interfaces/confirmationui in libteeui.
* Move support library to the new namespace teeui
* Removed IntegerSequence and replaced it with std::index_sequence
* Remove dependency on HIDL types hidl_vec, hidl_string, and
the Keymaster type HardwareAuthToken for the message passing between
the TA and the HAL. To this end this patch introduces static_vec.h.
static_vec is an alias for std::vector when compiled with
-DTEEUI_USE_STD_VEC which it should always be compiled with in the
context of the HAL service. Otherwise, it behaves like a span that
does not own the underlying storage otherwise. This is the expected
behavior when compiled in the context of the TA.
* Moved confirmationui HAL enums into the teeui namespace. It is now up to
the HAL implementation to translate between the TA types and the hidl
defined types.
* Separated the core logic for message formatting from the
protocol (now generic_messages.h) and serialization functions
for common types (now common_message_types.h).
* Add protocol specifiers to commands to allow easier extension of the
protocols between the TA and HAL.
* NullOr was removed in favor of std::optional.
Test: keystore_cli_v2
VtsHalConfirmationUIV1_0TargetTest
Bug: 111446692
Bug: 111451575
Change-Id: I8a498d49593b6e382c874d08f322f572c03ddcb8
diff --git a/libteeui/include/teeui/cbor.h b/libteeui/include/teeui/cbor.h
new file mode 100644
index 0000000..3b1320a
--- /dev/null
+++ b/libteeui/include/teeui/cbor.h
@@ -0,0 +1,296 @@
+/*
+ *
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEEUI_CBOR_H_
+#define TEEUI_CBOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <type_traits>
+
+namespace teeui {
+namespace cbor {
+
+template <typename In, typename Out> Out copy(In begin, In end, Out out) {
+ while (begin != end) {
+ *out++ = *begin++;
+ }
+ return out;
+}
+
+enum class Type : uint8_t {
+ NUMBER = 0,
+ NEGATIVE = 1,
+ BYTE_STRING = 2,
+ TEXT_STRING = 3,
+ ARRAY = 4,
+ MAP = 5,
+ TAG = 6,
+ FLOAT = 7,
+};
+
+enum class Error : uint32_t {
+ OK = 0,
+ OUT_OF_DATA = 1,
+ MALFORMED = 2,
+ MALFORMED_UTF8 = 3,
+};
+
+template <typename Key, typename Value> struct MapElement {
+ const Key& key_;
+ const Value& value_;
+ MapElement(const Key& key, const Value& value) : key_(key), value_(value) {}
+};
+
+template <typename... Elems> struct Array;
+
+template <typename Head, typename... Tail> struct Array<Head, Tail...> {
+ const Head& head_;
+ Array<Tail...> tail_;
+ Array(const Head& head, const Tail&... tail) : head_(head), tail_(tail...) {}
+ constexpr size_t size() const { return sizeof...(Tail) + 1; };
+};
+
+template <> struct Array<> {};
+
+struct TextStr {};
+struct ByteStr {};
+
+template <typename T, typename Variant> struct StringBuffer {
+ const T* data_;
+ size_t size_;
+ StringBuffer(const T* data, size_t size) : data_(data), size_(size) {
+ static_assert(sizeof(T) == 1, "elements too large");
+ }
+ const T* data() const { return data_; }
+ size_t size() const { return size_; }
+};
+
+/**
+ * Takes a char array turns it into a StringBuffer of TextStr type. The length of the resulting
+ * StringBuffer is size - 1, effectively stripping the 0 character from the region being considered.
+ * If the terminating 0 shall not be stripped use text_keep_last.
+ */
+template <size_t size> StringBuffer<char, TextStr> text(const char (&str)[size]) {
+ if (size > 0) return StringBuffer<char, TextStr>(str, size - 1);
+ return StringBuffer<char, TextStr>(str, size);
+}
+
+/**
+ * As opposed to text(const char (&str)[size] this function does not strips the last character.
+ */
+template <size_t size> StringBuffer<char, TextStr> text_keep_last(const char (&str)[size]) {
+ return StringBuffer<char, TextStr>(str, size);
+}
+
+template <typename T> auto getData(const T& v) -> decltype(v.data()) {
+ return v.data();
+}
+
+template <typename T> auto getData(const T& v) -> decltype(v.c_str()) {
+ return v.c_str();
+}
+
+template <typename T>
+auto text(const T& str) -> StringBuffer<std::decay_t<decltype(*getData(str))>, TextStr> {
+ return StringBuffer<std::decay_t<decltype(*getData(str))>, TextStr>(getData(str), str.size());
+}
+
+inline StringBuffer<char, TextStr> text(const char* str, size_t size) {
+ return StringBuffer<char, TextStr>(str, size);
+}
+
+template <typename T, size_t size> StringBuffer<T, ByteStr> bytes(const T (&str)[size]) {
+ return StringBuffer<T, ByteStr>(str, size);
+}
+
+template <typename T> StringBuffer<T, ByteStr> bytes(const T* str, size_t size) {
+ return StringBuffer<T, ByteStr>(str, size);
+}
+
+template <typename T>
+auto bytes(const T& str) -> StringBuffer<std::decay_t<decltype(*getData(str))>, ByteStr> {
+ return StringBuffer<std::decay_t<decltype(*getData(str))>, ByteStr>(getData(str), str.size());
+}
+
+template <typename... Elems> struct Map;
+
+template <typename HeadKey, typename HeadValue, typename... Tail>
+struct Map<MapElement<HeadKey, HeadValue>, Tail...> {
+ const MapElement<HeadKey, HeadValue>& head_;
+ Map<Tail...> tail_;
+ Map(const MapElement<HeadKey, HeadValue>& head, const Tail&... tail)
+ : head_(head), tail_(tail...) {}
+ constexpr size_t size() const { return sizeof...(Tail) + 1; };
+};
+
+template <> struct Map<> {};
+
+template <typename... Keys, typename... Values>
+Map<MapElement<Keys, Values>...> map(const MapElement<Keys, Values>&... elements) {
+ return Map<MapElement<Keys, Values>...>(elements...);
+}
+
+template <typename... Elements> Array<Elements...> arr(const Elements&... elements) {
+ return Array<Elements...>(elements...);
+}
+
+template <typename Key, typename Value> MapElement<Key, Value> pair(const Key& k, const Value& v) {
+ return MapElement<Key, Value>(k, v);
+}
+
+template <size_t size> struct getUnsignedType;
+
+template <> struct getUnsignedType<sizeof(uint8_t)> { using type = uint8_t; };
+template <> struct getUnsignedType<sizeof(uint16_t)> { using type = uint16_t; };
+template <> struct getUnsignedType<sizeof(uint32_t)> { using type = uint32_t; };
+template <> struct getUnsignedType<sizeof(uint64_t)> { using type = uint64_t; };
+
+template <size_t size> using Unsigned = typename getUnsignedType<size>::type;
+
+class WriteState {
+ public:
+ WriteState() : data_(nullptr), size_(0), error_(Error::OK) {}
+ WriteState(uint8_t* buffer, size_t size) : data_(buffer), size_(size), error_(Error::OK) {}
+ WriteState(uint8_t* buffer, size_t size, Error error)
+ : data_(buffer), size_(size), error_(error) {}
+ template <size_t size>
+ WriteState(uint8_t (&buffer)[size]) : data_(buffer), size_(size), error_(Error::OK) {}
+
+ WriteState& operator++() {
+ if (size_) {
+ ++data_;
+ --size_;
+ } else {
+ error_ = Error::OUT_OF_DATA;
+ }
+ return *this;
+ }
+ WriteState& operator+=(size_t offset) {
+ if (offset > size_) {
+ error_ = Error::OUT_OF_DATA;
+ } else {
+ data_ += offset;
+ size_ -= offset;
+ }
+ return *this;
+ }
+ operator bool() const { return error_ == Error::OK; }
+
+ uint8_t* data_;
+ size_t size_;
+ Error error_;
+};
+
+WriteState writeHeader(WriteState wState, Type type, const uint64_t value);
+bool checkUTF8Copy(const char* begin, const char* const end, uint8_t* out);
+
+template <typename T> WriteState writeNumber(WriteState wState, const T& v) {
+ if (!wState) return wState;
+ if (v >= 0) {
+ return writeHeader(wState, Type::NUMBER, v);
+ } else {
+ return writeHeader(wState, Type::NEGATIVE, UINT64_C(-1) - v);
+ }
+}
+
+inline WriteState write(const WriteState& wState, const uint8_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int8_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const uint16_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int16_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const uint32_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int32_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const uint64_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int64_t& v) {
+ return writeNumber(wState, v);
+}
+
+template <typename T> WriteState write(WriteState wState, const StringBuffer<T, TextStr>& v) {
+ wState = writeHeader(wState, Type::TEXT_STRING, v.size());
+ uint8_t* buffer = wState.data_;
+ wState += v.size();
+ if (!wState) return wState;
+ if (!checkUTF8Copy(v.data(), v.data() + v.size(), buffer)) {
+ wState.error_ = Error::MALFORMED_UTF8;
+ }
+ return wState;
+}
+
+template <typename T> WriteState write(WriteState wState, const StringBuffer<T, ByteStr>& v) {
+ wState = writeHeader(wState, Type::BYTE_STRING, v.size());
+ uint8_t* buffer = wState.data_;
+ wState += v.size();
+ if (!wState) return wState;
+ static_assert(sizeof(*v.data()) == 1, "elements too large");
+ copy(v.data(), v.data() + v.size(), buffer);
+ return wState;
+}
+
+template <template <typename...> class Arr>
+WriteState writeArrayHelper(WriteState wState, const Arr<>&) {
+ return wState;
+}
+
+template <template <typename...> class Arr, typename Head, typename... Tail>
+WriteState writeArrayHelper(WriteState wState, const Arr<Head, Tail...>& arr) {
+ wState = write(wState, arr.head_);
+ return writeArrayHelper(wState, arr.tail_);
+}
+
+template <typename... Elems> WriteState write(WriteState wState, const Map<Elems...>& map) {
+ if (!wState) return wState;
+ wState = writeHeader(wState, Type::MAP, map.size());
+ return writeArrayHelper(wState, map);
+}
+
+template <typename... Elems> WriteState write(WriteState wState, const Array<Elems...>& arr) {
+ if (!wState) return wState;
+ wState = writeHeader(wState, Type::ARRAY, arr.size());
+ return writeArrayHelper(wState, arr);
+}
+
+template <typename Key, typename Value>
+WriteState write(WriteState wState, const MapElement<Key, Value>& element) {
+ if (!wState) return wState;
+ wState = write(wState, element.key_);
+ return write(wState, element.value_);
+}
+
+template <typename Head, typename... Tail>
+WriteState write(WriteState wState, const Head& head, const Tail&... tail) {
+ wState = write(wState, head);
+ return write(wState, tail...);
+}
+
+} // namespace cbor
+} // namespace teeui
+
+#endif // TEEUI_CBOR_H_
diff --git a/libteeui/include/teeui/common_message_types.h b/libteeui/include/teeui/common_message_types.h
new file mode 100644
index 0000000..c9fb2a6
--- /dev/null
+++ b/libteeui/include/teeui/common_message_types.h
@@ -0,0 +1,178 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEEUI_COMMONMESSAGETYPES_H_
+#define TEEUI_COMMONMESSAGETYPES_H_
+
+#include <teeui/msg_formatting.h>
+
+#include <tuple>
+
+#include <stdint.h>
+
+#include <teeui/static_vec.h>
+
+namespace teeui {
+
+enum class UIOption : uint32_t;
+enum class ResponseCode : uint32_t;
+enum class MessageSize : uint32_t;
+enum class TestKeyBits : uint8_t;
+enum class TestModeCommands : uint64_t;
+
+enum class UIOption : uint32_t {
+ AccessibilityInverted = 0u,
+ AccessibilityMagnified = 1u,
+};
+
+enum class ResponseCode : uint32_t {
+ OK = 0u,
+ Canceled = 1u,
+ Aborted = 2u,
+ OperationPending = 3u,
+ Ignored = 4u,
+ SystemError = 5u,
+ Unimplemented = 6u,
+ Unexpected = 7u,
+ UIError = 0x10000,
+ UIErrorMissingGlyph,
+ UIErrorMessageTooLong,
+ UIErrorMalformedUTF8Encoding,
+};
+
+enum class MessageSize : uint32_t {
+ MAX = 6144u,
+};
+
+enum class TestKeyBits : uint8_t {
+ BYTE = 165,
+};
+
+enum class TestModeCommands : uint64_t {
+ OK_EVENT = 0ull,
+ CANCEL_EVENT = 1ull,
+};
+
+using MsgString = static_vec<const char>;
+template <typename T> using MsgVector = static_vec<T>;
+
+template <typename T> inline const uint8_t* copyField(T& field, const uint8_t*(&pos)) {
+ auto& s = bytesCast(field);
+ std::copy(pos, pos + sizeof(T), s);
+ return pos + sizeof(T);
+}
+
+template <typename T> inline uint8_t* copyField(const T& field, uint8_t*(&pos)) {
+ auto& s = bytesCast(field);
+ return std::copy(s, &s[sizeof(T)], pos);
+}
+
+/**
+ * This actually only reads in place if compiled without TEEUI_USE_STD_VECTOR. See static_vec.h
+ * If compiled with TEEUI_USE_STD_VECTOR MsgVector becomes std::vector and the data is actually
+ * copied.
+ */
+template <typename T> std::tuple<ReadStream, MsgVector<T>> readSimpleVecInPlace(ReadStream in) {
+ std::tuple<ReadStream, MsgVector<T>> result;
+ ReadStream::ptr_t pos = nullptr;
+ size_t read_size = 0;
+ std::tie(std::get<0>(result), pos, read_size) = read(in);
+ if (!std::get<0>(result) || read_size % sizeof(T)) {
+ std::get<0>(result).bad();
+ return result;
+ }
+ std::get<1>(result) =
+ MsgVector<T>(reinterpret_cast<T*>(const_cast<uint8_t*>(pos)),
+ reinterpret_cast<T*>(const_cast<uint8_t*>(pos)) + (read_size / sizeof(T)));
+ return result;
+}
+
+template <typename T> WriteStream writeSimpleVec(WriteStream out, const MsgVector<T>& vec) {
+ return write(out, reinterpret_cast<const uint8_t*>(vec.data()), vec.size() * sizeof(T));
+}
+
+// ResponseCode
+inline std::tuple<ReadStream, ResponseCode> read(Message<ResponseCode>, ReadStream in) {
+ return readSimpleType<ResponseCode>(in);
+}
+inline WriteStream write(WriteStream out, const ResponseCode& v) {
+ return write(out, bytesCast(v));
+}
+
+// TestModeCommands
+inline std::tuple<ReadStream, TestModeCommands> read(Message<TestModeCommands>, ReadStream in) {
+ return readSimpleType<TestModeCommands>(in);
+}
+inline WriteStream write(WriteStream out, const TestModeCommands& v) {
+ return write(out, bytesCast(v));
+}
+
+namespace msg {
+
+// MsgVector<uint8_t>
+inline std::tuple<ReadStream, MsgVector<uint8_t>> read(Message<MsgVector<uint8_t>>, ReadStream in) {
+ return readSimpleVecInPlace<uint8_t>(in);
+}
+inline WriteStream write(WriteStream out, const MsgVector<uint8_t>& v) {
+ return writeSimpleVec(out, v);
+}
+
+// MsgString
+inline std::tuple<ReadStream, MsgString> read(Message<MsgString>, ReadStream in) {
+ return readSimpleVecInPlace<const char>(in);
+}
+inline WriteStream write(WriteStream out, const MsgString& v) {
+ return writeSimpleVec(out, v);
+}
+
+// MsgVector<UIOption>
+inline std::tuple<ReadStream, MsgVector<UIOption>> read(Message<MsgVector<UIOption>>,
+ ReadStream in) {
+ return readSimpleVecInPlace<UIOption>(in);
+}
+inline WriteStream write(WriteStream out, const MsgVector<UIOption>& v) {
+ return writeSimpleVec(out, v);
+}
+
+} // namespace msg
+
+// teeui::Array<uint8_t, size>
+template <size_t size>
+inline std::tuple<teeui::ReadStream, teeui::Array<uint8_t, size>>
+read(teeui::Message<teeui::Array<uint8_t, size>>, teeui::ReadStream in) {
+ std::tuple<teeui::ReadStream, teeui::Array<uint8_t, size>> result;
+ teeui::ReadStream& in_ = std::get<0>(result);
+ auto& result_ = std::get<1>(result);
+ teeui::ReadStream::ptr_t pos = nullptr;
+ size_t read_size = 0;
+ std::tie(in_, pos, read_size) = read(in);
+ if (!in_) return result;
+ if (read_size != size) {
+ in_.bad();
+ return result;
+ }
+ std::copy(pos, pos + size, result_.data());
+ return result;
+}
+template <size_t size>
+inline teeui::WriteStream write(teeui::WriteStream out, const teeui::Array<uint8_t, size>& v) {
+ return write(out, v.data(), v.size());
+}
+
+} // namespace teeui
+
+#endif // TEEUI_COMMONMESSAGETYPES_H_
diff --git a/libteeui/include/teeui/generic_messages.h b/libteeui/include/teeui/generic_messages.h
new file mode 100644
index 0000000..90cc690
--- /dev/null
+++ b/libteeui/include/teeui/generic_messages.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEEUI_GENERICMESSAGES_H_
+#define TEEUI_GENERICMESSAGES_H_
+
+#include <limits>
+#include <tuple>
+
+#include <stdint.h>
+
+#include <teeui/common_message_types.h>
+#include <teeui/msg_formatting.h>
+
+namespace teeui {
+
+enum class Command : uint32_t {
+ Invalid,
+ PromptUserConfirmation,
+ FetchConfirmationResult,
+ DeliverTestCommand,
+ Abort,
+};
+
+using Protocol = uint32_t;
+
+template <Protocol Proto, typename CmdT, CmdT cmd> struct Cmd {};
+
+static constexpr const Protocol kProtoGeneric = 0;
+static constexpr const Protocol kProtoInvalid = std::numeric_limits<Protocol>::max();
+
+#define DECLARE_GENERIC_COMMAND(cmd) using Cmd##cmd = Cmd<kProtoGeneric, Command, Command::cmd>
+
+DECLARE_GENERIC_COMMAND(PromptUserConfirmation);
+DECLARE_GENERIC_COMMAND(FetchConfirmationResult);
+DECLARE_GENERIC_COMMAND(DeliverTestCommand);
+DECLARE_GENERIC_COMMAND(Abort);
+
+using PromptUserConfirmationMsg = Message<CmdPromptUserConfirmation, MsgString, MsgVector<uint8_t>,
+ MsgString, MsgVector<UIOption>>;
+using PromptUserConfirmationResponse = Message<ResponseCode>;
+using DeliverTestCommandMessage = Message<CmdDeliverTestCommand, TestModeCommands>;
+using DeliverTestCommandResponse = Message<ResponseCode>;
+using AbortMsg = Message<CmdAbort>;
+using ResultMsg = Message<ResponseCode, MsgVector<uint8_t>, MsgVector<uint8_t>>;
+using FetchConfirmationResult = Message<CmdFetchConfirmationResult>;
+
+template <uint32_t proto, typename CmdT, CmdT cmd>
+inline WriteStream write(WriteStream out, Cmd<proto, CmdT, cmd>) {
+ volatile Protocol* protoptr = reinterpret_cast<volatile Protocol*>(out.pos());
+ out += sizeof(Protocol);
+ if (out) *protoptr = proto;
+
+ volatile CmdT* cmdptr = reinterpret_cast<volatile CmdT*>(out.pos());
+ out += sizeof(CmdT);
+ if (out) *cmdptr = cmd;
+
+ return out;
+}
+
+template <typename CmdSpec, typename... Tail>
+WriteStream write(Message<CmdSpec, Tail...>, WriteStream out, const Tail&... tail) {
+ out = write(out, CmdSpec());
+ return write(Message<Tail...>(), out, tail...);
+}
+
+template <Protocol proto, typename CmdT, CmdT cmd, typename... Fields>
+std::tuple<ReadStream, Fields...> read(Message<Cmd<proto, CmdT, cmd>, Fields...>, ReadStream in) {
+ return read(Message<Fields...>(), in);
+}
+
+template <Protocol proto, typename CmdT, CmdT cmd, typename... T>
+struct msg2tuple<Message<Cmd<proto, CmdT, cmd>, T...>> {
+ using type = std::tuple<T...>;
+};
+
+std::tuple<ReadStream, uint32_t> readU32(ReadStream in);
+
+template <typename CmdT, CmdT Invalid = CmdT::Invalid>
+std::tuple<ReadStream, CmdT> readCmd(ReadStream in) {
+ static_assert(sizeof(CmdT) == sizeof(uint32_t),
+ "Can only be used with types the size of uint32_t");
+ auto [stream, value] = readU32(in);
+ if (stream) return {stream, CmdT(value)};
+ return {stream, Invalid};
+}
+
+template <typename CmdT, CmdT Invalid = CmdT::Invalid> CmdT peakCmd(ReadStream in) {
+ auto [_, cmd] = readCmd<CmdT>(in);
+ return cmd;
+}
+
+std::tuple<ReadStream, Command> readCommand(ReadStream in);
+Command peakCommand(ReadStream in);
+std::tuple<ReadStream, Protocol> readProtocol(ReadStream in);
+Protocol peakProtocol(ReadStream in);
+
+} // namespace teeui
+
+#endif // TEEUI_GENERICMESSAGES_H_
diff --git a/libteeui/include/teeui/generic_operation.h b/libteeui/include/teeui/generic_operation.h
new file mode 100644
index 0000000..e821e13
--- /dev/null
+++ b/libteeui/include/teeui/generic_operation.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEEUI_GENERICOPERATION_H_
+#define TEEUI_GENERICOPERATION_H_
+
+#include <teeui/cbor.h>
+#include <teeui/generic_messages.h>
+#include <teeui/msg_formatting.h>
+#include <teeui/utils.h>
+
+namespace teeui {
+
+inline bool hasOption(UIOption option, const MsgVector<UIOption>& uiOptions) {
+ for (auto& o : uiOptions) {
+ if (o == option) return true;
+ }
+ return false;
+}
+
+/**
+ * The generic Confirmation Operation:
+ *
+ * Derived needs to implement:
+ * Derived::TimeStamp needs to be a timestamp type
+ * Derived::now() needs to return a TimeStamp value denoting the current point in time.
+ * Derived::hmac256 with the following prototype which computes the 32-byte HMAC-SHA256 over the
+ * concatenation of all provided "buffers" keyed with "key".
+ *
+ * optional<Hmac> HMacImplementation::hmac256(const AuthTokenKey& key,
+ * std::initializer_list<ByteBufferProxy> buffers);
+ *
+ * ResponseCode initHook();
+ * Gets called on PromptUserConfirmation. If initHook returns anything but ResponseCode::OK,
+ * The operation is not started and the result is returned to the HAL service.
+ * void abortHook();
+ * Gets called on Abort. Allows the implementation to perform cleanup.
+ * void finalizeHook();
+ * Gets called on FetchConfirmationResult.
+ * ResponseCode testCommandHook(TestModeCommands testCmd);
+ * Gets called on DeliverTestCommand and allows the implementation to react to test commands.
+ *
+ * And optionally:
+ * WriteStream vendorCommandHook(ReadStream in, WriteStream out);
+ *
+ * The latter allows Implementations to implement custom protocol extensions.
+ *
+ */
+template <typename Derived, typename TimeStamp> class Operation {
+ using HMacer = HMac<Derived>;
+
+ public:
+ Operation()
+ : error_(ResponseCode::Ignored), formattedMessageLength_(0),
+ maginifiedViewRequested_(false), invertedColorModeRequested_(false),
+ languageIdLength_(0) {}
+
+ ResponseCode init(const MsgString& promptText, const MsgVector<uint8_t>& extraData,
+ const MsgString& locale, const MsgVector<UIOption>& options) {
+ // An hmacKey needs to be installed before we can commence operation.
+ if (!hmacKey_) return ResponseCode::Unexpected;
+ if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending;
+ confirmationTokenScratchpad_ = {};
+
+ // 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.begin(), promptText.end(), promptStringBuffer_);
+ *pos = 0; // null-terminate the prompt for the renderer.
+
+ using namespace ::teeui::cbor;
+ using CborError = ::teeui::cbor::Error;
+ // Note the extra data is accessed only once for formatting 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 CborError::OK:
+ break;
+ case CborError::OUT_OF_DATA:
+ return ResponseCode::UIErrorMessageTooLong;
+ case CborError::MALFORMED_UTF8:
+ return ResponseCode::UIErrorMalformedUTF8Encoding;
+ case CborError::MALFORMED:
+ default:
+ return ResponseCode::Unexpected;
+ }
+ formattedMessageLength_ = state.data_ - formattedMessageBuffer_;
+
+ if (locale.size() >= kMaxLocaleSize) return ResponseCode::UIErrorMessageTooLong;
+ pos = std::copy(locale.begin(), locale.end(), languageIdBuffer_);
+ *pos = 0;
+ languageIdLength_ = locale.size();
+
+ invertedColorModeRequested_ = hasOption(UIOption::AccessibilityInverted, options);
+ maginifiedViewRequested_ = hasOption(UIOption::AccessibilityMagnified, options);
+
+ // on success record the start time
+ startTime_ = Derived::now();
+ if (!startTime_.isOk()) {
+ return ResponseCode::SystemError;
+ }
+
+ auto rc = static_cast<Derived*>(this)->initHook();
+
+ if (rc == ResponseCode::OK) {
+ error_ = ResponseCode::OK;
+ }
+ return rc;
+ }
+
+ void setHmacKey(const AuthTokenKey& key) { hmacKey_ = key; }
+ optional<AuthTokenKey> hmacKey() const { return hmacKey_; }
+
+ void abort() {
+ if (isPending()) {
+ error_ = ResponseCode::Aborted;
+ static_cast<Derived*>(this)->abortHook();
+ }
+ }
+
+ void userCancel() {
+ if (isPending()) {
+ error_ = ResponseCode::Canceled;
+ }
+ }
+
+ std::tuple<ResponseCode, MsgVector<uint8_t>, MsgVector<uint8_t>> fetchConfirmationResult() {
+ std::tuple<ResponseCode, MsgVector<uint8_t>, MsgVector<uint8_t>> result;
+ auto& [rc, message, token] = result;
+ rc = error_;
+ if (error_ == ResponseCode::OK) {
+ message = {&formattedMessageBuffer_[0],
+ &formattedMessageBuffer_[formattedMessageLength_]};
+ if (confirmationTokenScratchpad_) {
+ token = {confirmationTokenScratchpad_->begin(),
+ confirmationTokenScratchpad_->end()};
+ }
+ }
+ error_ = ResponseCode::Ignored;
+ static_cast<Derived*>(this)->finalizeHook();
+ return result;
+ }
+
+ bool isPending() const { return error_ != ResponseCode::Ignored; }
+
+ const MsgString getPrompt() const {
+ return {&promptStringBuffer_[0], &promptStringBuffer_[strlen(promptStringBuffer_)]};
+ }
+
+ ResponseCode deliverTestCommand(TestModeCommands testCommand) {
+ constexpr const auto testKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE));
+ auto rc = static_cast<Derived*>(this)->testCommandHook(testCommand);
+ if (rc != ResponseCode::OK) return rc;
+ switch (testCommand) {
+ case TestModeCommands::OK_EVENT: {
+ if (isPending()) {
+ signConfirmation(testKey);
+ return ResponseCode::OK;
+ } else {
+ return ResponseCode::Ignored;
+ }
+ }
+ case TestModeCommands::CANCEL_EVENT: {
+ bool ignored = !isPending();
+ userCancel();
+ return ignored ? ResponseCode::Ignored : ResponseCode::OK;
+ }
+ default:
+ return ResponseCode::Ignored;
+ }
+ }
+
+ WriteStream dispatchCommandMessage(ReadStream in_, WriteStream out) {
+ auto [in, proto] = readProtocol(in_);
+ if (proto == kProtoGeneric) {
+ auto [in_cmd, cmd] = readCommand(in);
+ switch (cmd) {
+ case Command::PromptUserConfirmation:
+ return command(CmdPromptUserConfirmation(), in_cmd, out);
+ case Command::FetchConfirmationResult:
+ return command(CmdFetchConfirmationResult(), in_cmd, out);
+ case Command::DeliverTestCommand:
+ return command(CmdDeliverTestCommand(), in_cmd, out);
+ case Command::Abort:
+ return command(CmdAbort(), in_cmd, out);
+ default:
+ return write(Message<ResponseCode>(), out, ResponseCode::Unimplemented);
+ }
+ }
+ return static_cast<Derived*>(this)->extendedProtocolHook(proto, in, out);
+ }
+
+ protected:
+ /*
+ * The extendedProtocolHoock allows implementations to implement custom protocols on top of
+ * the default commands.
+ * This function is only called if "Derived" does not implement the extendedProtocolHoock
+ * and writes ResponseCode::Unimplemented to the response buffer.
+ */
+ inline WriteStream extendedProtocolHook(Protocol proto, ReadStream, WriteStream out) {
+ return write(Message<ResponseCode>(), out, ResponseCode::Unimplemented);
+ }
+
+ private:
+ WriteStream command(CmdPromptUserConfirmation, ReadStream in, WriteStream out) {
+ auto [in_, promt, extra, locale, options] = read(PromptUserConfirmationMsg(), in);
+ if (!in_) return write(PromptUserConfirmationResponse(), out, ResponseCode::SystemError);
+ auto rc = init(promt, extra, locale, options);
+ return write(PromptUserConfirmationResponse(), out, rc);
+ }
+ WriteStream command(CmdFetchConfirmationResult, ReadStream in, WriteStream out) {
+ auto [rc, message, token] = fetchConfirmationResult();
+ return write(ResultMsg(), out, rc, message, token);
+ }
+ WriteStream command(CmdDeliverTestCommand, ReadStream in, WriteStream out) {
+ auto [in_, token] = read(DeliverTestCommandMessage(), in);
+ if (!in_) return write(DeliverTestCommandResponse(), out, ResponseCode::SystemError);
+ auto rc = deliverTestCommand(token);
+ return write(DeliverTestCommandResponse(), out, rc);
+ }
+ WriteStream command(CmdAbort, ReadStream in, WriteStream out) {
+ abort();
+ return out;
+ }
+
+ MsgVector<uint8_t> getMessage() {
+ MsgVector<uint8_t> result;
+ if (error_ != ResponseCode::OK) return {};
+ return {&formattedMessageBuffer_[0], &formattedMessageBuffer_[formattedMessageLength_]};
+ }
+
+ protected:
+ void signConfirmation(const AuthTokenKey& key) {
+ if (error_ != ResponseCode::OK) return;
+ confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage());
+ if (!confirmationTokenScratchpad_) {
+ error_ = ResponseCode::Unexpected;
+ }
+ }
+
+ protected:
+ ResponseCode error_ = ResponseCode::Ignored;
+ uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)];
+ size_t formattedMessageLength_ = 0;
+ char promptStringBuffer_[uint32_t(MessageSize::MAX)];
+ optional<Hmac> confirmationTokenScratchpad_;
+ TimeStamp startTime_;
+ optional<AuthTokenKey> hmacKey_;
+ bool maginifiedViewRequested_;
+ bool invertedColorModeRequested_;
+ constexpr static size_t kMaxLocaleSize = 64;
+ char languageIdBuffer_[kMaxLocaleSize];
+ size_t languageIdLength_;
+};
+
+} // namespace teeui
+
+#endif // CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
diff --git a/libteeui/include/teeui/msg_formatting.h b/libteeui/include/teeui/msg_formatting.h
new file mode 100644
index 0000000..1644c2f
--- /dev/null
+++ b/libteeui/include/teeui/msg_formatting.h
@@ -0,0 +1,202 @@
+/*
+ *
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEEUI_MSG_FORMATTING_H_
+#define TEEUI_MSG_FORMATTING_H_
+
+#include <algorithm>
+#include <stddef.h>
+#include <stdint.h>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <teeui/utils.h>
+
+namespace teeui {
+namespace msg {
+
+template <typename... fields> class Message {};
+
+template <size_t... idx, typename... T>
+std::tuple<std::remove_reference_t<T>&&...> tuple_move_helper(std::index_sequence<idx...>,
+ std::tuple<T...>&& t) {
+ return {std::move(std::get<idx>(t))...};
+}
+
+template <typename... T>
+std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>&& t) {
+ return tuple_move_helper(std::make_index_sequence<sizeof...(T)>(), std::move(t));
+}
+
+template <typename... T>
+std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>& t) {
+ return tuple_move_helper(std::make_index_sequence<sizeof...(T)>(), std::move(t));
+}
+
+void zero(volatile uint8_t* begin, const volatile uint8_t* end);
+
+template <typename T> class StreamState {
+ public:
+ static_assert(
+ sizeof(T) == 1,
+ "StreamState must be instantiated with 1 byte sized type, e.g., uint8_t or const uint8_t.");
+ using ptr_t = T*;
+ template <size_t size>
+ StreamState(T (&buffer)[size]) : begin_(&buffer[0]), end_(&buffer[size]), pos_(begin_) {}
+ StreamState(T* buffer, size_t size) : begin_(buffer), end_(buffer + size), pos_(begin_) {}
+ StreamState() : begin_(nullptr), end_(nullptr), pos_(nullptr) {}
+ StreamState& operator+=(size_t offset) {
+ auto good_ = pos_ != nullptr && pos_ + offset <= end_;
+ if (good_) {
+ pos_ += offset;
+ } else {
+ pos_ = nullptr;
+ }
+ return *this;
+ }
+
+ operator bool() const { return pos_ != nullptr; }
+ ptr_t pos() const { return pos_; };
+
+ template <typename U = T>
+ bool insertFieldSize(typename std::enable_if<!std::is_const<U>::value, uint32_t>::type size) {
+ // offset to the nearest n * 8 + 4 boundary from beginning of the buffer! (not memory).
+ uintptr_t pos = pos_ - begin_;
+ auto offset = (((pos + 11UL) & ~7UL) - 4UL) - pos;
+ if (*this += offset + sizeof(size)) {
+ // zero out the gaps
+ zero(pos_ - offset - sizeof(size), pos_ - sizeof(size));
+ *reinterpret_cast<uint32_t*>(pos_ - sizeof(size)) = size;
+ return true;
+ }
+ return false;
+ }
+
+ template <typename U = T>
+ typename std::enable_if<std::is_const<U>::value, uint32_t>::type extractFieldSize() {
+ // offset to the nearest n * 8 + 4 boundary from beginning of the buffer! (not memory).
+ uintptr_t pos = pos_ - begin_;
+ auto offset = (((pos + 11UL) & ~7UL) - 4UL) - pos;
+ if (*this += offset + sizeof(uint32_t)) {
+ return *reinterpret_cast<const uint32_t*>(pos_ - sizeof(uint32_t));
+ }
+ return 0;
+ }
+
+ void bad() { pos_ = nullptr; };
+
+ private:
+ ptr_t begin_;
+ ptr_t end_;
+ ptr_t pos_;
+};
+
+using WriteStream = StreamState<uint8_t>;
+using ReadStream = StreamState<const uint8_t>;
+
+inline void zero(const volatile uint8_t*, const volatile uint8_t*) {}
+//// This odd alignment function aligns the stream position to a 4byte and never 8byte boundary
+//// It is to accommodate the 4 byte size field which is then followed by 8byte aligned data.
+// template <typename T>
+// StreamState<T> unalign(StreamState<T> s) {
+// auto result = s;
+// auto offset = (((uintptr_t(s.pos_) + 11UL) & ~7UL) - 4UL) - uintptr_t(s.pos_);
+// result += offset;
+// // zero out the gaps when writing
+// if (result) zero(s.pos_, result.pos_);
+// return result;
+//}
+
+WriteStream write(WriteStream out, const uint8_t* buffer, uint32_t size);
+
+template <uint32_t size> WriteStream write(WriteStream out, const uint8_t (&v)[size]) {
+ return write(out, v, size);
+}
+
+std::tuple<ReadStream, ReadStream::ptr_t, uint32_t> read(ReadStream in);
+
+template <typename T> std::tuple<ReadStream, T> readSimpleType(ReadStream in) {
+ auto [in_, pos, size] = read(in);
+ T result = {};
+ if (in_ && size == sizeof(T))
+ result = *reinterpret_cast<const T*>(pos);
+ else
+ in_.bad();
+ return {in_, result};
+}
+
+inline WriteStream write(Message<>, WriteStream out) {
+ return out;
+}
+
+template <typename Head, typename... Tail>
+WriteStream write(Message<Head, Tail...>, WriteStream out, const Head& head, const Tail&... tail) {
+ out = write(out, head);
+ return write(Message<Tail...>(), out, tail...);
+}
+
+template <typename... Msg> std::tuple<ReadStream, Msg...> read(Message<Msg...>, ReadStream in) {
+ return {in, [&in]() -> Msg {
+ Msg result;
+ std::tie(in, result) = read(Message<Msg>(), in);
+ return result;
+ }()...};
+}
+
+template <typename T> struct msg2tuple {};
+
+template <typename... T> struct msg2tuple<Message<T...>> { using type = std::tuple<T...>; };
+
+template <typename T> using msg2tuple_t = typename msg2tuple<T>::type;
+
+template <size_t first_idx, size_t... idx, typename HEAD, typename... T>
+std::tuple<T&&...> tuple_tail(std::index_sequence<first_idx, idx...>, std::tuple<HEAD, T...>&& t) {
+ return {std::move(std::get<idx>(t))...};
+}
+
+template <size_t first_idx, size_t... idx, typename HEAD, typename... T>
+std::tuple<const T&...> tuple_tail(std::index_sequence<first_idx, idx...>,
+ const std::tuple<HEAD, T...>& t) {
+ return {std::get<idx>(t)...};
+}
+
+template <typename HEAD, typename... Tail>
+std::tuple<Tail&&...> tuple_tail(std::tuple<HEAD, Tail...>&& t) {
+ return tuple_tail(std::make_index_sequence<sizeof...(Tail) + 1>(), std::move(t));
+}
+
+template <typename HEAD, typename... Tail>
+std::tuple<const Tail&...> tuple_tail(const std::tuple<HEAD, Tail...>& t) {
+ return tuple_tail(std::make_index_sequence<sizeof...(Tail) + 1>(), t);
+}
+
+} // namespace msg
+
+using msg::Message;
+using msg::msg2tuple;
+using msg::msg2tuple_t;
+using msg::read;
+using msg::readSimpleType;
+using msg::ReadStream;
+using msg::tuple_tail;
+using msg::write;
+using msg::WriteStream;
+
+} // namespace teeui
+
+#endif // TEEUI_MSG_FORMATTING_H_
diff --git a/libteeui/include/teeui/static_vec.h b/libteeui/include/teeui/static_vec.h
new file mode 100644
index 0000000..fb41935
--- /dev/null
+++ b/libteeui/include/teeui/static_vec.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEEUI_STATIC_VEC_H_
+#define TEEUI_STATIC_VEC_H_
+
+#include <stddef.h> // for size_t
+
+#ifdef TEEUI_USE_STD_VECTOR
+#include <vector>
+#endif
+
+namespace teeui {
+
+/**
+ * teeui::static_vec leads a double life.
+ *
+ * When compiling with TEEUI_USE_STD_VECTOR it is just an alias for std::vector. HAL services using
+ * this library must use this option for safe handling of message buffers.
+ *
+ * When compiling without TEEUI_USE_STD_VECTOR this class works more like a span that does not
+ * actually own the buffer if wraps. This is the behavior expected by generic_operation.h, which
+ * is used inside a heap-less implementation of a confirmationui trusted app.
+ */
+#ifndef TEEUI_USE_STD_VECTOR
+template <typename T> class static_vec {
+ private:
+ T* data_;
+ size_t size_;
+
+ public:
+ static_vec() : data_(nullptr), size_(0) {}
+ static_vec(T* begin, T* end) : data_(begin), size_(end - begin) {}
+ template <size_t s> static_vec(T (&arr)[s]) : data_(&arr[0]), size_(s) {}
+ static_vec(const static_vec&) = default;
+ static_vec(static_vec&&) = default;
+ static_vec& operator=(const static_vec&) = default;
+ static_vec& operator=(static_vec&&) = default;
+
+ T* data() { return data_; }
+ const T* data() const { return data_; }
+ size_t size() const { return size_; }
+
+ T* begin() { return data_; }
+ T* end() { return data_ + size_; }
+ const T* begin() const { return data_; }
+ const T* end() const { return data_ + size_; }
+};
+#else
+template <typename T> using static_vec = std::vector<T>;
+#endif
+
+} // namespace teeui
+
+#endif // TEEUI_STATIC_VEC_H_
diff --git a/libteeui/include/teeui/utils.h b/libteeui/include/teeui/utils.h
index c6e5f6c..7e1b908 100644
--- a/libteeui/include/teeui/utils.h
+++ b/libteeui/include/teeui/utils.h
@@ -19,10 +19,12 @@
#define TEEUI_LIBTEEUI_UTILS_H_
#include <math.h>
+#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <algorithm>
+#include <initializer_list>
#include <optional>
#include <tuple>
#include <type_traits>
@@ -34,6 +36,135 @@
using std::optional;
+template <typename T, size_t elements> class Array {
+ using array_type = T[elements];
+
+ public:
+ constexpr Array() : data_{} {}
+ constexpr Array(const T (&data)[elements]) { std::copy(data, data + elements, data_); }
+ constexpr Array(const std::initializer_list<uint8_t>& li) {
+ size_t i = 0;
+ for (auto& item : li) {
+ data_[i] = item;
+ ++i;
+ if (i == elements) break;
+ }
+ for (; i < elements; ++i) {
+ data_[i] = {};
+ }
+ }
+
+ T* data() { return data_; }
+ const T* data() const { return data_; }
+ constexpr size_t size() const { return elements; }
+
+ T* begin() { return data_; }
+ T* end() { return data_ + elements; }
+ const T* begin() const { return data_; }
+ const T* end() const { return data_ + elements; }
+
+ static constexpr Array fill(const T& v) {
+ Array result;
+ for (size_t i = 0; i < elements; ++i) {
+ result.data_[i] = v;
+ }
+ return result;
+ }
+
+ private:
+ array_type data_;
+};
+
+template <typename T> auto bytesCast(const T& v) -> const uint8_t (&)[sizeof(T)] {
+ return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
+}
+template <typename T> auto bytesCast(T& v) -> uint8_t (&)[sizeof(T)] {
+ return *reinterpret_cast<uint8_t(*)[sizeof(T)]>(&v);
+}
+
+class ByteBufferProxy {
+ template <typename T> struct has_data {
+ template <typename U> static int f(const U*, const void*) { return 0; }
+ template <typename U> static int* f(const U* u, decltype(u->data())) { return nullptr; }
+ static constexpr bool value = std::is_pointer<decltype(f((T*)nullptr, ""))>::value;
+ };
+
+ public:
+ template <typename T>
+ ByteBufferProxy(const T& buffer, decltype(buffer.data()) = nullptr)
+ : data_(reinterpret_cast<const uint8_t*>(buffer.data())), size_(buffer.size()) {
+ static_assert(sizeof(decltype(*buffer.data())) == 1, "elements to large");
+ }
+
+ template <size_t size>
+ ByteBufferProxy(const char (&buffer)[size])
+ : data_(reinterpret_cast<const uint8_t*>(buffer)), size_(size - 1) {
+ static_assert(size > 0, "even an empty string must be 0-terminated");
+ }
+
+ template <size_t size>
+ ByteBufferProxy(const uint8_t (&buffer)[size]) : data_(buffer), size_(size) {}
+
+ ByteBufferProxy() : data_(nullptr), size_(0) {}
+
+ const uint8_t* data() const { return data_; }
+ size_t size() const { return size_; }
+
+ const uint8_t* begin() const { return data_; }
+ const uint8_t* end() const { return data_ + size_; }
+
+ private:
+ const uint8_t* data_;
+ size_t size_;
+};
+
+constexpr const uint8_t kAuthTokenKeySize = 32;
+constexpr const uint8_t kHmacKeySize = kAuthTokenKeySize;
+using AuthTokenKey = Array<uint8_t, kAuthTokenKeySize>;
+using Hmac = AuthTokenKey;
+
+/**
+ * Implementer are expected to provide an implementation with the following prototype:
+ * static optional<array<uint8_t, 32>> hmac256(const uint8_t key[32],
+ * std::initializer_list<ByteBufferProxy> buffers);
+ */
+template <typename Impl> class HMac {
+ public:
+ template <typename... Data>
+ static optional<Hmac> hmac256(const AuthTokenKey& key, const Data&... data) {
+ return Impl::hmac256(key, {data...});
+ }
+};
+
+bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs);
+
+template <typename IntType, uint32_t byteOrder> struct choose_hton;
+
+template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
+ inline static IntType hton(const IntType& value) {
+ IntType result = {};
+ const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
+ unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
+ for (int i = sizeof(IntType) - 1; i >= 0; --i) {
+ *(outbytes++) = inbytes[i];
+ }
+ return result;
+ }
+};
+
+template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
+ inline static IntType hton(const IntType& value) { return value; }
+};
+
+template <typename IntType> inline IntType hton(const IntType& value) {
+ return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
+}
+
+template <typename IntType> inline IntType ntoh(const IntType& value) {
+ // same operation as hton
+ return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
+}
+
enum class Unit : uint8_t {
PX,
DP,
diff --git a/libteeui/src/cbor.cpp b/libteeui/src/cbor.cpp
new file mode 100644
index 0000000..48c461d
--- /dev/null
+++ b/libteeui/src/cbor.cpp
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <teeui/cbor.h>
+
+namespace teeui {
+namespace cbor {
+
+namespace {
+
+inline uint8_t getByte(const uint64_t& v, const uint8_t index) {
+ return (v >> (index * 8)) & 0xff;
+}
+
+WriteState writeBytes(WriteState state, uint64_t value, uint8_t size) {
+ auto pos = state.data_;
+ if (!(state += size)) return state;
+ switch (size) {
+ case 8:
+ *pos++ = getByte(value, 7);
+ *pos++ = getByte(value, 6);
+ *pos++ = getByte(value, 5);
+ *pos++ = getByte(value, 4);
+ [[fallthrough]];
+ case 4:
+ *pos++ = getByte(value, 3);
+ *pos++ = getByte(value, 2);
+ [[fallthrough]];
+ case 2:
+ *pos++ = getByte(value, 1);
+ [[fallthrough]];
+ case 1:
+ *pos++ = getByte(value, 0);
+ break;
+ default:
+ state.error_ = Error::MALFORMED;
+ }
+ return state;
+}
+
+} // anonymous namespace
+
+WriteState writeHeader(WriteState wState, Type type, const uint64_t value) {
+ if (!wState) return wState;
+ uint8_t& header = *wState.data_;
+ if (!++wState) return wState;
+ header = static_cast<uint8_t>(type) << 5;
+ if (value < 24) {
+ header |= static_cast<uint8_t>(value);
+ } else if (value < 0x100) {
+ header |= 24;
+ wState = writeBytes(wState, value, 1);
+ } else if (value < 0x10000) {
+ header |= 25;
+ wState = writeBytes(wState, value, 2);
+ } else if (value < 0x100000000) {
+ header |= 26;
+ wState = writeBytes(wState, value, 4);
+ } else {
+ header |= 27;
+ wState = writeBytes(wState, value, 8);
+ }
+ return wState;
+}
+
+static size_t byteCount(char c) {
+ if ((0xc0 & c) == 0x80) {
+ return 0; // this is a multibyte payload byte
+ } else if (0x80 & c) {
+ /*
+ * CLZ - count leading zeroes.
+ * __builtin_clz promotes the argument to unsigned int.
+ * We invert c to turn leading ones into leading zeroes.
+ * We subtract additional leading zeroes due to the type promotion from the result.
+ */
+ return __builtin_clz((unsigned char)(~c)) - (sizeof(unsigned int) * 8 - 8);
+ } else {
+ return 1;
+ }
+}
+
+bool checkUTF8Copy(const char* begin, const char* const end, uint8_t* out) {
+ while (begin != end) {
+ auto bc = byteCount(*begin);
+ // if the string ends in the middle of a multi byte char it is invalid
+ if (begin + bc > end) return false;
+ switch (bc) {
+ case 4:
+ if (out) *out++ = *reinterpret_cast<const uint8_t*>(begin++);
+ [[fallthrough]];
+ case 3:
+ if (out) *out++ = *reinterpret_cast<const uint8_t*>(begin++);
+ [[fallthrough]];
+ case 2:
+ if (out) *out++ = *reinterpret_cast<const uint8_t*>(begin++);
+ [[fallthrough]];
+ case 1:
+ if (out) *out++ = *reinterpret_cast<const uint8_t*>(begin++);
+ break;
+ default:
+ // case 0 means we encounted a payload byte when we expected a header.
+ // case > 4 is malformed.
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace cbor
+} // namespace teeui
diff --git a/libteeui/src/generic_messages.cpp b/libteeui/src/generic_messages.cpp
new file mode 100644
index 0000000..e6da5e8
--- /dev/null
+++ b/libteeui/src/generic_messages.cpp
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <teeui/generic_messages.h>
+
+namespace teeui {
+
+std::tuple<ReadStream, uint32_t> readU32(ReadStream in) {
+ volatile const uint32_t* pos = reinterpret_cast<volatile const uint32_t*>(in.pos());
+ in += sizeof(uint32_t);
+ if (!in) return {in, 0};
+ return {in, *pos};
+}
+
+std::tuple<ReadStream, Command> readCommand(ReadStream in) {
+ return readCmd<Command>(in);
+}
+
+Command peakCommand(ReadStream in) {
+ auto [_, cmd] = readCommand(in);
+ return cmd;
+}
+
+std::tuple<ReadStream, Protocol> readProtocol(ReadStream in) {
+ return readCmd<Protocol, kProtoInvalid>(in);
+}
+
+Protocol peakProtocol(ReadStream in) {
+ auto [_, proto] = readProtocol(in);
+ return proto;
+}
+
+} // namespace teeui
diff --git a/libteeui/src/msg_formatting.cpp b/libteeui/src/msg_formatting.cpp
new file mode 100644
index 0000000..b96cea4
--- /dev/null
+++ b/libteeui/src/msg_formatting.cpp
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <teeui/msg_formatting.h>
+
+namespace teeui {
+namespace msg {
+
+void zero(volatile uint8_t* begin, const volatile uint8_t* end) {
+ while (begin != end) {
+ *begin++ = 0x0;
+ }
+}
+
+WriteStream write(WriteStream out, const uint8_t* buffer, uint32_t size) {
+ if (out.insertFieldSize(size)) {
+ auto pos = out.pos();
+ out += size;
+ if (out) {
+ std::copy(buffer, buffer + size, pos);
+ }
+ }
+ return out;
+}
+
+std::tuple<ReadStream, ReadStream::ptr_t, uint32_t> read(ReadStream in) {
+ auto size = in.extractFieldSize();
+ auto pos = in.pos();
+ in += size;
+ return {in, pos, size};
+}
+
+} // namespace msg
+} // namespace teeui
diff --git a/libteeui/src/utils.cpp b/libteeui/src/utils.cpp
index d094bf7..8e8c2ee 100644
--- a/libteeui/src/utils.cpp
+++ b/libteeui/src/utils.cpp
@@ -19,6 +19,17 @@
namespace teeui {
+bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs) {
+ if (lhs.size() == rhs.size()) {
+ auto lhsi = lhs.begin();
+ auto rhsi = rhs.begin();
+ while (lhsi != lhs.end()) {
+ if (*lhsi++ != *rhsi++) return false;
+ }
+ }
+ return true;
+}
+
constexpr const DefaultNumericType kEpsilon = 0.000001;
constexpr const DefaultNumericType kHalfSqrt2 = 0.70710678118;