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 
27 namespace android {
28 namespace hardware {
29 namespace confirmationui {
30 namespace 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  */
39 template <typename ValueT>
40 class NullOr {
41     template <typename T>
42     struct reference_initializer {
initreference_initializer43         static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
44     };
45     template <typename T>
46     struct pointer_initializer {
initpointer_initializer47         static T init() { return nullptr; }
48     };
49     template <typename T>
50     struct value_initializer {
initvalue_initializer51         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:
NullOr()60     NullOr() : value_(initializer_t<ValueT>::init()), null_(true) {}
61     template <typename T>
NullOr(T && value)62     NullOr(T&& value) : value_(std::forward<T>(value)), null_(false) {}
63 
isOk()64     bool isOk() const { return !null_; }
65 
value()66     const ValueT& value() const & { return value_; }
value()67     ValueT& value() & { return value_; }
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 
78 template <typename T, size_t elements>
79 class array {
80     using array_type = T[elements];
81 
82    public:
array()83     array() : data_{} {}
array(const T (& data)[elements])84     array(const T (&data)[elements]) { std::copy(data, data + elements, data_); }
array(const T & v)85     explicit array(const T& v) { fill(v); }
86 
data()87     T* data() { return data_; }
data()88     const T* data() const { return data_; }
size()89     constexpr size_t size() const { return elements; }
90 
begin()91     T* begin() { return data_; }
end()92     T* end() { return data_ + elements; }
begin()93     const T* begin() const { return data_; }
end()94     const T* end() const { return data_ + elements; }
95 
fill(const T & v)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 
106 template <typename T>
107 auto bytes_cast(const T& v) -> const uint8_t (&)[sizeof(T)] {
108     return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
109 }
110 template <typename T>
111 auto bytes_cast(T& v) -> uint8_t (&)[sizeof(T)] {
112     return *reinterpret_cast<uint8_t(*)[sizeof(T)]>(&v);
113 }
114 
115 class ByteBufferProxy {
116     template <typename T>
117     struct has_data {
118         template <typename U>
fhas_data119         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>
ByteBufferProxy(const char (& buffer)[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>
ByteBufferProxy(const uint8_t (& buffer)[size])152     ByteBufferProxy(const uint8_t (&buffer)[size]) : data_(buffer), size_(size) {}
153 
ByteBufferProxy()154     ByteBufferProxy() : data_(nullptr), size_(0) {}
155 
data()156     const uint8_t* data() const { return data_; }
size()157     size_t size() const { return size_; }
158 
begin()159     const uint8_t* begin() const { return data_; }
end()160     const uint8_t* end() const { return data_ + size_; }
161 
162    private:
163     const uint8_t* data_;
164     size_t size_;
165 };
166 
167 constexpr uint8_t auth_token_key_size = 32;
168 constexpr uint8_t hmac_size_bytes = support::auth_token_key_size;
169 using auth_token_key_t = array<uint8_t, auth_token_key_size>;
170 using 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  */
177 template <typename Impl>
178 class HMac {
179    public:
180     template <typename... Data>
hmac256(const auth_token_key_t & key,const Data &...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 
186 bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs);
187 
188 template <typename IntType, uint32_t byteOrder>
189 struct choose_hton;
190 
191 template <typename IntType>
192 struct 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 
204 template <typename IntType>
205 struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
206     inline static IntType hton(const IntType& value) { return value; }
207 };
208 
209 template <typename IntType>
210 inline IntType hton(const IntType& value) {
211     return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
212 }
213 
214 template <typename IntType>
215 inline 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