blob: 6558799bb7668163a6975af43628b31ad7c5095a [file] [log] [blame]
/*
**
** 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 CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_
#define CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_
#include <android/hardware/confirmationui/1.0/types.h>
#include <android/hardware/keymaster/4.0/types.h>
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <tuple>
#include <type_traits>
#include <android/hardware/confirmationui/support/confirmationui_utils.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace support {
template <size_t... I>
class IntegerSequence {};
namespace integer_sequence {
template <typename Lhs, typename Rhs>
struct conc {};
template <size_t... ILhs, size_t... IRhs>
struct conc<IntegerSequence<ILhs...>, IntegerSequence<IRhs...>> {
using type = IntegerSequence<ILhs..., IRhs...>;
};
template <typename Lhs, typename Rhs>
using conc_t = typename conc<Lhs, Rhs>::type;
template <size_t... n>
struct make {};
template <size_t n>
struct make<n> {
using type = conc_t<typename make<n - 1>::type, IntegerSequence<n - 1>>;
};
template <size_t start, size_t n>
struct make<start, n> {
using type = conc_t<typename make<start, n - 1>::type, IntegerSequence<start + n - 1>>;
};
template <size_t start>
struct make<start, start> {
using type = IntegerSequence<start>;
};
template <>
struct make<0> {
using type = IntegerSequence<>;
};
template <size_t... n>
using make_t = typename make<n...>::type;
} // namespace integer_sequence
template <size_t... idx, typename... T>
std::tuple<std::remove_reference_t<T>&&...> tuple_move_helper(IntegerSequence<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(integer_sequence::make_t<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(integer_sequence::make_t<sizeof...(T)>(), std::move(t));
}
using ::android::hardware::confirmationui::V1_0::ResponseCode;
using ::android::hardware::confirmationui::V1_0::UIOption;
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
template <typename... fields>
class Message {};
enum class Command : uint32_t {
PromptUserConfirmation,
DeliverSecureInputEvent,
Abort,
Vendor,
};
template <Command cmd>
struct Cmd {};
#define DECLARE_COMMAND(cmd) using cmd##_t = Cmd<Command::cmd>
DECLARE_COMMAND(PromptUserConfirmation);
DECLARE_COMMAND(DeliverSecureInputEvent);
DECLARE_COMMAND(Abort);
DECLARE_COMMAND(Vendor);
using PromptUserConfirmationMsg = Message<PromptUserConfirmation_t, hidl_string, hidl_vec<uint8_t>,
hidl_string, hidl_vec<UIOption>>;
using PromptUserConfirmationResponse = Message<ResponseCode>;
using DeliverSecureInputEventMsg = Message<DeliverSecureInputEvent_t, HardwareAuthToken>;
using DeliverSecureInputEventRespose = Message<ResponseCode>;
using AbortMsg = Message<Abort_t>;
using ResultMsg = Message<ResponseCode, hidl_vec<uint8_t>, hidl_vec<uint8_t>>;
template <typename T>
struct StreamState {
using ptr_t = volatile T*;
volatile T* pos_;
size_t bytes_left_;
bool good_;
template <size_t size>
StreamState(T (&buffer)[size]) : pos_(buffer), bytes_left_(size), good_(size > 0) {}
StreamState(T* buffer, size_t size) : pos_(buffer), bytes_left_(size), good_(size > 0) {}
StreamState() : pos_(nullptr), bytes_left_(0), good_(false) {}
StreamState& operator++() {
if (good_ && bytes_left_) {
++pos_;
--bytes_left_;
} else {
good_ = false;
}
return *this;
}
StreamState& operator+=(size_t offset) {
if (!good_ || offset > bytes_left_) {
good_ = false;
} else {
pos_ += offset;
bytes_left_ -= offset;
}
return *this;
}
operator bool() const { return good_; }
volatile T* pos() const { return pos_; };
};
using WriteStream = StreamState<uint8_t>;
using ReadStream = StreamState<const uint8_t>;
inline void zero(volatile uint8_t* begin, const volatile uint8_t* end) {
while (begin != end) {
*begin++ = 0xaa;
}
}
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) {
uint8_t unalignment = uintptr_t(s.pos_) & 0x3;
auto pos = s.pos_;
if (unalignment) {
s += 4 - unalignment;
}
// now s.pos_ is aligned on a 4byte boundary
if ((uintptr_t(s.pos_) & 0x4) == 0) {
// if we are 8byte aligned add 4
s += 4;
}
// zero out the gaps when writing
zero(pos, s.pos_);
return s;
}
inline WriteStream write(WriteStream out, const uint8_t* buffer, size_t size) {
auto pos = out.pos();
uint32_t v = size;
out += 4 + size;
if (out) {
if (size != v) {
out.good_ = false;
return out;
}
auto& s = bytes_cast(v);
pos = std::copy(s, s + 4, pos);
std::copy(buffer, buffer + size, pos);
}
return out;
}
template <size_t size>
WriteStream write(WriteStream out, const uint8_t (&v)[size]) {
return write(out, v, size);
}
inline std::tuple<ReadStream, ReadStream::ptr_t, size_t> read(ReadStream in) {
auto pos = in.pos();
in += 4;
if (!in) return {in, nullptr, 0};
uint32_t size;
std::copy(pos, pos + 4, bytes_cast(size));
pos = in.pos();
in += size;
if (!in) return {in, nullptr, 0};
return {in, pos, size};
}
template <typename T>
std::tuple<ReadStream, T> readSimpleType(ReadStream in) {
T result;
ReadStream::ptr_t pos = nullptr;
size_t read_size = 0;
std::tie(in, pos, read_size) = read(in);
if (!in || read_size != sizeof(T)) {
in.good_ = false;
return {in, {}};
}
std::copy(pos, pos + sizeof(T), bytes_cast(result));
return {in, std::move(result)};
}
template <typename T>
std::tuple<ReadStream, hidl_vec<T>> readSimpleHidlVecInPlace(ReadStream in) {
std::tuple<ReadStream, hidl_vec<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).good_ = false;
return result;
}
std::get<1>(result).setToExternal(reinterpret_cast<T*>(const_cast<uint8_t*>(pos)),
read_size / sizeof(T));
return result;
}
template <typename T>
WriteStream writeSimpleHidlVec(WriteStream out, const hidl_vec<T>& vec) {
return write(out, reinterpret_cast<const uint8_t*>(vec.data()), vec.size() * sizeof(T));
}
// HardwareAuthToken
constexpr size_t hatSizeNoMac() {
HardwareAuthToken* hat = nullptr;
return sizeof hat->challenge + sizeof hat->userId + sizeof hat->authenticatorId +
sizeof hat->authenticatorType + sizeof hat->timestamp;
}
template <typename T>
inline volatile const uint8_t* copyField(T& field, volatile const uint8_t*(&pos)) {
auto& s = bytes_cast(field);
std::copy(pos, pos + sizeof(T), s);
return pos + sizeof(T);
}
inline std::tuple<ReadStream, HardwareAuthToken> read(Message<HardwareAuthToken>, ReadStream in_) {
std::tuple<ReadStream, HardwareAuthToken> result;
ReadStream& in = std::get<0>(result) = in_;
auto& hat = std::get<1>(result);
constexpr size_t hatSize = hatSizeNoMac();
ReadStream::ptr_t pos = nullptr;
size_t read_size = 0;
std::tie(in, pos, read_size) = read(in);
if (!in || read_size != hatSize) {
in.good_ = false;
return result;
}
pos = copyField(hat.challenge, pos);
pos = copyField(hat.userId, pos);
pos = copyField(hat.authenticatorId, pos);
pos = copyField(hat.authenticatorType, pos);
pos = copyField(hat.timestamp, pos);
std::tie(in, hat.mac) = readSimpleHidlVecInPlace<uint8_t>(in);
return result;
}
template <typename T>
inline volatile uint8_t* copyField(const T& field, volatile uint8_t*(&pos)) {
auto& s = bytes_cast(field);
return std::copy(s, &s[sizeof(T)], pos);
}
inline WriteStream write(WriteStream out, const HardwareAuthToken& v) {
auto pos = out.pos();
uint32_t size_field = hatSizeNoMac();
out += 4 + size_field;
if (!out) return out;
pos = copyField(size_field, pos);
pos = copyField(v.challenge, pos);
pos = copyField(v.userId, pos);
pos = copyField(v.authenticatorId, pos);
pos = copyField(v.authenticatorType, pos);
pos = copyField(v.timestamp, pos);
return writeSimpleHidlVec(out, v.mac);
}
// 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, bytes_cast(v));
}
// hidl_vec<uint8_t>
inline std::tuple<ReadStream, hidl_vec<uint8_t>> read(Message<hidl_vec<uint8_t>>, ReadStream in) {
return readSimpleHidlVecInPlace<uint8_t>(in);
}
inline WriteStream write(WriteStream out, const hidl_vec<uint8_t>& v) {
return writeSimpleHidlVec(out, v);
}
// hidl_vec<UIOption>
inline std::tuple<ReadStream, hidl_vec<UIOption>> read(Message<hidl_vec<UIOption>>, ReadStream in) {
in = unalign(in);
return readSimpleHidlVecInPlace<UIOption>(in);
}
inline WriteStream write(WriteStream out, const hidl_vec<UIOption>& v) {
out = unalign(out);
return writeSimpleHidlVec(out, v);
}
// hidl_string
inline std::tuple<ReadStream, hidl_string> read(Message<hidl_string>, ReadStream in) {
std::tuple<ReadStream, hidl_string> result;
ReadStream& in_ = std::get<0>(result);
hidl_string& result_ = std::get<1>(result);
ReadStream::ptr_t pos = nullptr;
size_t read_size = 0;
std::tie(in_, pos, read_size) = read(in);
auto terminating_zero = in_.pos();
++in_; // skip the terminating zero. Does nothing if the stream was already bad
if (!in_) return result;
if (*terminating_zero) {
in_.good_ = false;
return result;
}
result_.setToExternal(reinterpret_cast<const char*>(const_cast<const uint8_t*>(pos)),
read_size);
return result;
}
inline WriteStream write(WriteStream out, const hidl_string& v) {
out = write(out, reinterpret_cast<const uint8_t*>(v.c_str()), v.size());
auto terminating_zero = out.pos();
++out;
if (out) {
*terminating_zero = 0;
}
return out;
}
inline WriteStream write(WriteStream out, Command cmd) {
volatile Command* pos = reinterpret_cast<volatile Command*>(out.pos_);
out += sizeof(Command);
if (out) {
*pos = cmd;
}
return out;
}
template <Command cmd>
WriteStream write(WriteStream out, Cmd<cmd>) {
return write(out, cmd);
}
inline std::tuple<ReadStream, bool> read(ReadStream in, Command cmd) {
volatile const Command* pos = reinterpret_cast<volatile const Command*>(in.pos_);
in += sizeof(Command);
if (!in) return {in, false};
return {in, *pos == cmd};
}
template <Command cmd>
std::tuple<ReadStream, bool> read(Message<Cmd<cmd>>, ReadStream in) {
return read(in, cmd);
}
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 <Command cmd, typename... Tail>
WriteStream write(Message<Cmd<cmd>, Tail...>, WriteStream out, const Tail&... tail) {
out = write(out, cmd);
return write(Message<Tail...>(), out, tail...);
}
template <Command cmd, typename HEAD, typename... Tail>
std::tuple<ReadStream, bool, HEAD, Tail...> read(Message<Cmd<cmd>, HEAD, Tail...>, ReadStream in) {
bool command_matches;
std::tie(in, command_matches) = read(in, cmd);
if (!command_matches) return {in, false, HEAD(), Tail()...};
return {in, true,
[&]() -> HEAD {
HEAD result;
std::tie(in, result) = read(Message<HEAD>(), in);
return result;
}(),
[&]() -> Tail {
Tail result;
std::tie(in, result) = read(Message<Tail>(), in);
return result;
}()...};
}
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 <Command cmd, typename... T>
struct msg2tuple<Message<Cmd<cmd>, T...>> {
using type = std::tuple<T...>;
};
template <typename T>
using msg2tuple_t = typename msg2tuple<T>::type;
template <size_t... idx, typename HEAD, typename... T>
std::tuple<T&&...> tuple_tail(IntegerSequence<idx...>, std::tuple<HEAD, T...>&& t) {
return {std::move(std::get<idx>(t))...};
}
template <size_t... idx, typename HEAD, typename... T>
std::tuple<const T&...> tuple_tail(IntegerSequence<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(integer_sequence::make_t<1, sizeof...(Tail)>(), std::move(t));
}
template <typename HEAD, typename... Tail>
std::tuple<const Tail&...> tuple_tail(const std::tuple<HEAD, Tail...>& t) {
return tuple_tail(integer_sequence::make_t<1, sizeof...(Tail)>(), t);
}
} // namespace support
} // namespace confirmationui
} // namespace hardware
} // namespace android
#endif // CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_