1/* 2** 3** Copyright 2017, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18#ifndef CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_ 19#define CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_ 20 21#include <android/hardware/confirmationui/1.0/types.h> 22#include <android/hardware/keymaster/4.0/types.h> 23#include <stddef.h> 24#include <stdint.h> 25#include <algorithm> 26#include <tuple> 27#include <type_traits> 28 29#include <android/hardware/confirmationui/support/confirmationui_utils.h> 30 31namespace android { 32namespace hardware { 33namespace confirmationui { 34namespace support { 35 36template <size_t... I> 37class IntegerSequence {}; 38 39namespace integer_sequence { 40 41template <typename Lhs, typename Rhs> 42struct conc {}; 43 44template <size_t... ILhs, size_t... IRhs> 45struct conc<IntegerSequence<ILhs...>, IntegerSequence<IRhs...>> { 46 using type = IntegerSequence<ILhs..., IRhs...>; 47}; 48 49template <typename Lhs, typename Rhs> 50using conc_t = typename conc<Lhs, Rhs>::type; 51 52template <size_t... n> 53struct make {}; 54 55template <size_t n> 56struct make<n> { 57 using type = conc_t<typename make<n - 1>::type, IntegerSequence<n - 1>>; 58}; 59template <size_t start, size_t n> 60struct make<start, n> { 61 using type = conc_t<typename make<start, n - 1>::type, IntegerSequence<start + n - 1>>; 62}; 63 64template <size_t start> 65struct make<start, start> { 66 using type = IntegerSequence<start>; 67}; 68 69template <> 70struct make<0> { 71 using type = IntegerSequence<>; 72}; 73 74template <size_t... n> 75using make_t = typename make<n...>::type; 76 77} // namespace integer_sequence 78 79template <size_t... idx, typename... T> 80std::tuple<std::remove_reference_t<T>&&...> tuple_move_helper(IntegerSequence<idx...>, 81 std::tuple<T...>&& t) { 82 return {std::move(std::get<idx>(t))...}; 83} 84 85template <typename... T> 86std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>&& t) { 87 return tuple_move_helper(integer_sequence::make_t<sizeof...(T)>(), std::move(t)); 88} 89 90template <typename... T> 91std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>& t) { 92 return tuple_move_helper(integer_sequence::make_t<sizeof...(T)>(), std::move(t)); 93} 94 95using ::android::hardware::confirmationui::V1_0::ResponseCode; 96using ::android::hardware::confirmationui::V1_0::UIOption; 97using ::android::hardware::keymaster::V4_0::HardwareAuthToken; 98using ::android::hardware::hidl_string; 99using ::android::hardware::hidl_vec; 100 101template <typename... fields> 102class Message {}; 103 104enum class Command : uint32_t { 105 PromptUserConfirmation, 106 DeliverSecureInputEvent, 107 Abort, 108 Vendor, 109}; 110 111template <Command cmd> 112struct Cmd {}; 113 114#define DECLARE_COMMAND(cmd) using cmd##_t = Cmd<Command::cmd> 115 116DECLARE_COMMAND(PromptUserConfirmation); 117DECLARE_COMMAND(DeliverSecureInputEvent); 118DECLARE_COMMAND(Abort); 119DECLARE_COMMAND(Vendor); 120 121using PromptUserConfirmationMsg = Message<PromptUserConfirmation_t, hidl_string, hidl_vec<uint8_t>, 122 hidl_string, hidl_vec<UIOption>>; 123using PromptUserConfirmationResponse = Message<ResponseCode>; 124using DeliverSecureInputEventMsg = Message<DeliverSecureInputEvent_t, HardwareAuthToken>; 125using DeliverSecureInputEventRespose = Message<ResponseCode>; 126using AbortMsg = Message<Abort_t>; 127using ResultMsg = Message<ResponseCode, hidl_vec<uint8_t>, hidl_vec<uint8_t>>; 128 129template <typename T> 130struct StreamState { 131 using ptr_t = volatile T*; 132 volatile T* pos_; 133 size_t bytes_left_; 134 bool good_; 135 template <size_t size> 136 StreamState(T (&buffer)[size]) : pos_(buffer), bytes_left_(size), good_(size > 0) {} 137 StreamState(T* buffer, size_t size) : pos_(buffer), bytes_left_(size), good_(size > 0) {} 138 StreamState() : pos_(nullptr), bytes_left_(0), good_(false) {} 139 StreamState& operator++() { 140 if (good_ && bytes_left_) { 141 ++pos_; 142 --bytes_left_; 143 } else { 144 good_ = false; 145 } 146 return *this; 147 } 148 StreamState& operator+=(size_t offset) { 149 if (!good_ || offset > bytes_left_) { 150 good_ = false; 151 } else { 152 pos_ += offset; 153 bytes_left_ -= offset; 154 } 155 return *this; 156 } 157 operator bool() const { return good_; } 158 volatile T* pos() const { return pos_; }; 159}; 160 161using WriteStream = StreamState<uint8_t>; 162using ReadStream = StreamState<const uint8_t>; 163 164inline void zero(volatile uint8_t* begin, const volatile uint8_t* end) { 165 while (begin != end) { 166 *begin++ = 0xaa; 167 } 168} 169inline void zero(const volatile uint8_t*, const volatile uint8_t*) {} 170// This odd alignment function aligns the stream position to a 4byte and never 8byte boundary 171// It is to accommodate the 4 byte size field which is then followed by 8byte aligned data. 172template <typename T> 173StreamState<T> unalign(StreamState<T> s) { 174 uint8_t unalignment = uintptr_t(s.pos_) & 0x3; 175 auto pos = s.pos_; 176 if (unalignment) { 177 s += 4 - unalignment; 178 } 179 // now s.pos_ is aligned on a 4byte boundary 180 if ((uintptr_t(s.pos_) & 0x4) == 0) { 181 // if we are 8byte aligned add 4 182 s += 4; 183 } 184 // zero out the gaps when writing 185 zero(pos, s.pos_); 186 return s; 187} 188 189inline WriteStream write(WriteStream out, const uint8_t* buffer, size_t size) { 190 auto pos = out.pos(); 191 uint32_t v = size; 192 out += 4 + size; 193 if (out) { 194 if (size != v) { 195 out.good_ = false; 196 return out; 197 } 198 auto& s = bytes_cast(v); 199 pos = std::copy(s, s + 4, pos); 200 std::copy(buffer, buffer + size, pos); 201 } 202 return out; 203} 204template <size_t size> 205WriteStream write(WriteStream out, const uint8_t (&v)[size]) { 206 return write(out, v, size); 207} 208 209inline std::tuple<ReadStream, ReadStream::ptr_t, size_t> read(ReadStream in) { 210 auto pos = in.pos(); 211 in += 4; 212 if (!in) return {in, nullptr, 0}; 213 uint32_t size; 214 std::copy(pos, pos + 4, bytes_cast(size)); 215 pos = in.pos(); 216 in += size; 217 if (!in) return {in, nullptr, 0}; 218 return {in, pos, size}; 219} 220 221template <typename T> 222std::tuple<ReadStream, T> readSimpleType(ReadStream in) { 223 T result; 224 ReadStream::ptr_t pos = nullptr; 225 size_t read_size = 0; 226 std::tie(in, pos, read_size) = read(in); 227 if (!in || read_size != sizeof(T)) { 228 in.good_ = false; 229 return {in, {}}; 230 } 231 std::copy(pos, pos + sizeof(T), bytes_cast(result)); 232 return {in, std::move(result)}; 233} 234 235template <typename T> 236std::tuple<ReadStream, hidl_vec<T>> readSimpleHidlVecInPlace(ReadStream in) { 237 std::tuple<ReadStream, hidl_vec<T>> result; 238 ReadStream::ptr_t pos = nullptr; 239 size_t read_size = 0; 240 std::tie(std::get<0>(result), pos, read_size) = read(in); 241 if (!std::get<0>(result) || read_size % sizeof(T)) { 242 std::get<0>(result).good_ = false; 243 return result; 244 } 245 std::get<1>(result).setToExternal(reinterpret_cast<T*>(const_cast<uint8_t*>(pos)), 246 read_size / sizeof(T)); 247 return result; 248} 249 250template <typename T> 251WriteStream writeSimpleHidlVec(WriteStream out, const hidl_vec<T>& vec) { 252 return write(out, reinterpret_cast<const uint8_t*>(vec.data()), vec.size() * sizeof(T)); 253} 254 255// HardwareAuthToken 256constexpr size_t hatSizeNoMac() { 257 HardwareAuthToken* hat = nullptr; 258 return sizeof hat->challenge + sizeof hat->userId + sizeof hat->authenticatorId + 259 sizeof hat->authenticatorType + sizeof hat->timestamp; 260} 261 262template <typename T> 263inline volatile const uint8_t* copyField(T& field, volatile const uint8_t*(&pos)) { 264 auto& s = bytes_cast(field); 265 std::copy(pos, pos + sizeof(T), s); 266 return pos + sizeof(T); 267} 268inline std::tuple<ReadStream, HardwareAuthToken> read(Message<HardwareAuthToken>, ReadStream in_) { 269 std::tuple<ReadStream, HardwareAuthToken> result; 270 ReadStream& in = std::get<0>(result) = in_; 271 auto& hat = std::get<1>(result); 272 constexpr size_t hatSize = hatSizeNoMac(); 273 ReadStream::ptr_t pos = nullptr; 274 size_t read_size = 0; 275 std::tie(in, pos, read_size) = read(in); 276 if (!in || read_size != hatSize) { 277 in.good_ = false; 278 return result; 279 } 280 pos = copyField(hat.challenge, pos); 281 pos = copyField(hat.userId, pos); 282 pos = copyField(hat.authenticatorId, pos); 283 pos = copyField(hat.authenticatorType, pos); 284 pos = copyField(hat.timestamp, pos); 285 std::tie(in, hat.mac) = readSimpleHidlVecInPlace<uint8_t>(in); 286 return result; 287} 288 289template <typename T> 290inline volatile uint8_t* copyField(const T& field, volatile uint8_t*(&pos)) { 291 auto& s = bytes_cast(field); 292 return std::copy(s, &s[sizeof(T)], pos); 293} 294 295inline WriteStream write(WriteStream out, const HardwareAuthToken& v) { 296 auto pos = out.pos(); 297 uint32_t size_field = hatSizeNoMac(); 298 out += 4 + size_field; 299 if (!out) return out; 300 pos = copyField(size_field, pos); 301 pos = copyField(v.challenge, pos); 302 pos = copyField(v.userId, pos); 303 pos = copyField(v.authenticatorId, pos); 304 pos = copyField(v.authenticatorType, pos); 305 pos = copyField(v.timestamp, pos); 306 return writeSimpleHidlVec(out, v.mac); 307} 308 309// ResponseCode 310inline std::tuple<ReadStream, ResponseCode> read(Message<ResponseCode>, ReadStream in) { 311 return readSimpleType<ResponseCode>(in); 312} 313inline WriteStream write(WriteStream out, const ResponseCode& v) { 314 return write(out, bytes_cast(v)); 315} 316 317// hidl_vec<uint8_t> 318inline std::tuple<ReadStream, hidl_vec<uint8_t>> read(Message<hidl_vec<uint8_t>>, ReadStream in) { 319 return readSimpleHidlVecInPlace<uint8_t>(in); 320} 321inline WriteStream write(WriteStream out, const hidl_vec<uint8_t>& v) { 322 return writeSimpleHidlVec(out, v); 323} 324 325// hidl_vec<UIOption> 326inline std::tuple<ReadStream, hidl_vec<UIOption>> read(Message<hidl_vec<UIOption>>, ReadStream in) { 327 in = unalign(in); 328 return readSimpleHidlVecInPlace<UIOption>(in); 329} 330inline WriteStream write(WriteStream out, const hidl_vec<UIOption>& v) { 331 out = unalign(out); 332 return writeSimpleHidlVec(out, v); 333} 334 335// hidl_string 336inline std::tuple<ReadStream, hidl_string> read(Message<hidl_string>, ReadStream in) { 337 std::tuple<ReadStream, hidl_string> result; 338 ReadStream& in_ = std::get<0>(result); 339 hidl_string& result_ = std::get<1>(result); 340 ReadStream::ptr_t pos = nullptr; 341 size_t read_size = 0; 342 std::tie(in_, pos, read_size) = read(in); 343 auto terminating_zero = in_.pos(); 344 ++in_; // skip the terminating zero. Does nothing if the stream was already bad 345 if (!in_) return result; 346 if (*terminating_zero) { 347 in_.good_ = false; 348 return result; 349 } 350 result_.setToExternal(reinterpret_cast<const char*>(const_cast<const uint8_t*>(pos)), 351 read_size); 352 return result; 353} 354inline WriteStream write(WriteStream out, const hidl_string& v) { 355 out = write(out, reinterpret_cast<const uint8_t*>(v.c_str()), v.size()); 356 auto terminating_zero = out.pos(); 357 ++out; 358 if (out) { 359 *terminating_zero = 0; 360 } 361 return out; 362} 363 364inline WriteStream write(WriteStream out, Command cmd) { 365 volatile Command* pos = reinterpret_cast<volatile Command*>(out.pos_); 366 out += sizeof(Command); 367 if (out) { 368 *pos = cmd; 369 } 370 return out; 371} 372template <Command cmd> 373WriteStream write(WriteStream out, Cmd<cmd>) { 374 return write(out, cmd); 375} 376 377inline std::tuple<ReadStream, bool> read(ReadStream in, Command cmd) { 378 volatile const Command* pos = reinterpret_cast<volatile const Command*>(in.pos_); 379 in += sizeof(Command); 380 if (!in) return {in, false}; 381 return {in, *pos == cmd}; 382} 383 384template <Command cmd> 385std::tuple<ReadStream, bool> read(Message<Cmd<cmd>>, ReadStream in) { 386 return read(in, cmd); 387} 388 389inline WriteStream write(Message<>, WriteStream out) { 390 return out; 391} 392 393template <typename Head, typename... Tail> 394WriteStream write(Message<Head, Tail...>, WriteStream out, const Head& head, const Tail&... tail) { 395 out = write(out, head); 396 return write(Message<Tail...>(), out, tail...); 397} 398 399template <Command cmd, typename... Tail> 400WriteStream write(Message<Cmd<cmd>, Tail...>, WriteStream out, const Tail&... tail) { 401 out = write(out, cmd); 402 return write(Message<Tail...>(), out, tail...); 403} 404 405template <Command cmd, typename HEAD, typename... Tail> 406std::tuple<ReadStream, bool, HEAD, Tail...> read(Message<Cmd<cmd>, HEAD, Tail...>, ReadStream in) { 407 bool command_matches; 408 std::tie(in, command_matches) = read(in, cmd); 409 if (!command_matches) return {in, false, HEAD(), Tail()...}; 410 411 return {in, true, 412 [&]() -> HEAD { 413 HEAD result; 414 std::tie(in, result) = read(Message<HEAD>(), in); 415 return result; 416 }(), 417 [&]() -> Tail { 418 Tail result; 419 std::tie(in, result) = read(Message<Tail>(), in); 420 return result; 421 }()...}; 422} 423 424template <typename... Msg> 425std::tuple<ReadStream, Msg...> read(Message<Msg...>, ReadStream in) { 426 return {in, [&in]() -> Msg { 427 Msg result; 428 std::tie(in, result) = read(Message<Msg>(), in); 429 return result; 430 }()...}; 431} 432 433template <typename T> 434struct msg2tuple {}; 435 436template <typename... T> 437struct msg2tuple<Message<T...>> { 438 using type = std::tuple<T...>; 439}; 440template <Command cmd, typename... T> 441struct msg2tuple<Message<Cmd<cmd>, T...>> { 442 using type = std::tuple<T...>; 443}; 444 445template <typename T> 446using msg2tuple_t = typename msg2tuple<T>::type; 447 448template <size_t... idx, typename HEAD, typename... T> 449std::tuple<T&&...> tuple_tail(IntegerSequence<idx...>, std::tuple<HEAD, T...>&& t) { 450 return {std::move(std::get<idx>(t))...}; 451} 452 453template <size_t... idx, typename HEAD, typename... T> 454std::tuple<const T&...> tuple_tail(IntegerSequence<idx...>, const std::tuple<HEAD, T...>& t) { 455 return {std::get<idx>(t)...}; 456} 457 458template <typename HEAD, typename... Tail> 459std::tuple<Tail&&...> tuple_tail(std::tuple<HEAD, Tail...>&& t) { 460 return tuple_tail(integer_sequence::make_t<1, sizeof...(Tail)>(), std::move(t)); 461} 462 463template <typename HEAD, typename... Tail> 464std::tuple<const Tail&...> tuple_tail(const std::tuple<HEAD, Tail...>& t) { 465 return tuple_tail(integer_sequence::make_t<1, sizeof...(Tail)>(), t); 466} 467 468} // namespace support 469} // namespace confirmationui 470} // namespace hardware 471} // namespace android 472 473#endif // CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_ 474