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_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
19#define CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
20
21#include <stddef.h>
22#include <stdint.h>
23#include <algorithm>
24#include <initializer_list>
25#include <type_traits>
26
27namespace android {
28namespace hardware {
29namespace confirmationui {
30namespace support {
31
32/**
33 * This class wraps a (mostly return) value and stores whether or not the wrapped value is valid out
34 * of band. Note that if the wrapped value is a reference it is unsafe to access the value if
35 * !isOk(). If the wrapped type is a pointer or value and !isOk(), it is still safe to access the
36 * wrapped value. In this case the pointer will be NULL though, and the value will be default
37 * constructed.
38 */
39template <typename ValueT>
40class NullOr {
41    template <typename T>
42    struct reference_initializer {
43        static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
44    };
45    template <typename T>
46    struct pointer_initializer {
47        static T init() { return nullptr; }
48    };
49    template <typename T>
50    struct value_initializer {
51        static T init() { return T(); }
52    };
53    template <typename T>
54    using initializer_t =
55        std::conditional_t<std::is_lvalue_reference<T>::value, reference_initializer<T>,
56                           std::conditional_t<std::is_pointer<T>::value, pointer_initializer<T>,
57                                              value_initializer<T>>>;
58
59   public:
60    NullOr() : value_(initializer_t<ValueT>::init()), null_(true) {}
61    template <typename T>
62    NullOr(T&& value) : value_(std::forward<T>(value)), null_(false) {}
63
64    bool isOk() const { return !null_; }
65
66    const ValueT& value() const & { return value_; }
67    ValueT& value() & { return value_; }
68    ValueT&& value() && { return std::move(value_); }
69
70    const std::remove_reference_t<ValueT>* operator->() const { return &value_; }
71    std::remove_reference_t<ValueT>* operator->() { return &value_; }
72
73   private:
74    ValueT value_;
75    bool null_;
76};
77
78template <typename T, size_t elements>
79class array {
80    using array_type = T[elements];
81
82   public:
83    array() : data_{} {}
84    array(const T (&data)[elements]) { std::copy(data, data + elements, data_); }
85    explicit array(const T& v) { fill(v); }
86
87    T* data() { return data_; }
88    const T* data() const { return data_; }
89    constexpr size_t size() const { return elements; }
90
91    T* begin() { return data_; }
92    T* end() { return data_ + elements; }
93    const T* begin() const { return data_; }
94    const T* end() const { return data_ + elements; }
95
96    void fill(const T& v) {
97        for (size_t i = 0; i < elements; ++i) {
98            data_[i] = v;
99        }
100    }
101
102   private:
103    array_type data_;
104};
105
106template <typename T>
107auto bytes_cast(const T& v) -> const uint8_t (&)[sizeof(T)] {
108    return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
109}
110template <typename T>
111auto bytes_cast(T& v) -> uint8_t (&)[sizeof(T)] {
112    return *reinterpret_cast<uint8_t(*)[sizeof(T)]>(&v);
113}
114
115class ByteBufferProxy {
116    template <typename T>
117    struct has_data {
118        template <typename U>
119        static int f(const U*, const void*) {
120            return 0;
121        }
122        template <typename U>
123        static int* f(const U* u, decltype(u->data())) {
124            return nullptr;
125        }
126        static constexpr bool value = std::is_pointer<decltype(f((T*)nullptr, ""))>::value;
127    };
128
129   public:
130    template <typename T>
131    ByteBufferProxy(const T& buffer, decltype(buffer.data()) = nullptr)
132        : data_(reinterpret_cast<const uint8_t*>(buffer.data())), size_(buffer.size()) {
133        static_assert(sizeof(decltype(*buffer.data())) == 1, "elements to large");
134    }
135
136    // this overload kicks in for types that have .c_str() but not .data(), such as hidl_string.
137    // std::string has both so we need to explicitly disable this overload if .data() is present.
138    template <typename T>
139    ByteBufferProxy(const T& buffer,
140                    std::enable_if_t<!has_data<T>::value, decltype(buffer.c_str())> = nullptr)
141        : data_(reinterpret_cast<const uint8_t*>(buffer.c_str())), size_(buffer.size()) {
142        static_assert(sizeof(decltype(*buffer.c_str())) == 1, "elements to large");
143    }
144
145    template <size_t size>
146    ByteBufferProxy(const char (&buffer)[size])
147        : data_(reinterpret_cast<const uint8_t*>(buffer)), size_(size - 1) {
148        static_assert(size > 0, "even an empty string must be 0-terminated");
149    }
150
151    template <size_t size>
152    ByteBufferProxy(const uint8_t (&buffer)[size]) : data_(buffer), size_(size) {}
153
154    ByteBufferProxy() : data_(nullptr), size_(0) {}
155
156    const uint8_t* data() const { return data_; }
157    size_t size() const { return size_; }
158
159    const uint8_t* begin() const { return data_; }
160    const uint8_t* end() const { return data_ + size_; }
161
162   private:
163    const uint8_t* data_;
164    size_t size_;
165};
166
167constexpr uint8_t auth_token_key_size = 32;
168constexpr uint8_t hmac_size_bytes = support::auth_token_key_size;
169using auth_token_key_t = array<uint8_t, auth_token_key_size>;
170using hmac_t = auth_token_key_t;
171
172/**
173 * Implementer are expected to provide an implementation with the following prototype:
174 *  static NullOr<array<uint8_t, 32>> hmac256(const uint8_t key[32],
175 *                                     std::initializer_list<ByteBufferProxy> buffers);
176 */
177template <typename Impl>
178class HMac {
179   public:
180    template <typename... Data>
181    static NullOr<hmac_t> hmac256(const auth_token_key_t& key, const Data&... data) {
182        return Impl::hmac256(key, {data...});
183    }
184};
185
186bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs);
187
188template <typename IntType, uint32_t byteOrder>
189struct choose_hton;
190
191template <typename IntType>
192struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
193    inline static IntType hton(const IntType& value) {
194        IntType result = {};
195        const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
196        unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
197        for (int i = sizeof(IntType) - 1; i >= 0; --i) {
198            *(outbytes++) = inbytes[i];
199        }
200        return result;
201    }
202};
203
204template <typename IntType>
205struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
206    inline static IntType hton(const IntType& value) { return value; }
207};
208
209template <typename IntType>
210inline IntType hton(const IntType& value) {
211    return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
212}
213
214template <typename IntType>
215inline IntType ntoh(const IntType& value) {
216    // same operation as hton
217    return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
218}
219
220}  // namespace support
221}  // namespace confirmationui
222}  // namespace hardware
223}  // namespace android
224
225#endif  // CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
226