Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a9f0fb0d authored by Janis Danisevskis's avatar Janis Danisevskis
Browse files

Add confirmation UI support libaray

Includes:

A light weight CBOR generator
  This patch adds a header only CBOR generator and unit tests.
  It allows expressing CBOR datastructures in C++ for subsequent
  serialization. The implementation performs no memory allocation
  and only depends on stdint.h (for (u)intx_t) and stddefs.h (for size_t).
  It is tailored for use in constrained environments such as TEEs.

Convenience method for generating a SHA256 HMAC

Bug: 63928580
Test: android.hardware.confirmationui@support-lib-tests
Change-Id: I1d93a85503f861281e71e09b1ede5cbb74219694
parent b70f2b25
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
//
// Copyright (C) 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.
//

cc_library {
    name: "android.hardware.confirmationui-support-lib",
    vendor_available: true,
    host_supported: true,
    vndk: {
        enabled: true,
    },
    srcs: [
        "src/cbor.cpp",
        "src/confirmationui_utils.cpp",
    ],
    export_include_dirs: [
        "include",
    ]
}

cc_test {
    name: "android.hardware.confirmationui-support-lib-tests",
    srcs: [
        "test/gtest_main.cpp",
        "test/android_cbor_test.cpp",
        "test/msg_formatting_test.cpp",
    ],
    static_libs: [
        "libgtest",
        "android.hardware.confirmationui-support-lib",
    ],
    shared_libs: [
        "android.hardware.confirmationui@1.0",
        "android.hardware.keymaster@4.0",
        "libhidlbase",
    ],
    clang: true,
    cflags: [ "-O0" ],
}
+2 −0
Original line number Diff line number Diff line
jdanis@google.com
swillden@google.com
+188 −0
Original line number Diff line number Diff line
/*
**
** 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_1_0_DEFAULT_GENERICOPERATION_H_
#define CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_

#include <android/hardware/confirmationui/1.0/types.h>
#include <android/hardware/confirmationui/support/cbor.h>
#include <android/hardware/confirmationui/support/confirmationui_utils.h>
#include <android/hardware/keymaster/4.0/types.h>

namespace android {
namespace hardware {
namespace confirmationui {
namespace V1_0 {
namespace generic {

namespace {
using namespace ::android::hardware::confirmationui::support;
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;

inline bool hasOption(UIOption option, const hidl_vec<UIOption>& uiOptions) {
    for (auto& o : uiOptions) {
        if (o == option) return true;
    }
    return false;
}

template <typename Callback, typename TimeStamper, typename HmacImplementation>
class Operation {
    using HMacer = support::HMac<HmacImplementation>;

   public:
    Operation() : error_(ResponseCode::Ignored), formattedMessageLength_(0) {}

    ResponseCode init(const Callback& resultCB, const hidl_string& promptText,
                      const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
                      const hidl_vec<UIOption>& uiOptions) {
        (void)locale;
        (void)uiOptions;
        resultCB_ = resultCB;
        if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending;
        // TODO make copy of promptText before using it may reside in shared buffer
        auto state = write(
            WriteState(formattedMessageBuffer_),
            map(pair(text("prompt"), text(promptText)), pair(text("extra"), bytes(extraData))));
        switch (state.error_) {
            case Error::OK:
                break;
            case Error::OUT_OF_DATA:
                return ResponseCode::UIErrorMessageTooLong;
            case Error::MALFORMED_UTF8:
                return ResponseCode::UIErrorMalformedUTF8Encoding;
            case Error::MALFORMED:
            default:
                return ResponseCode::Unexpected;
        }
        formattedMessageLength_ = state.data_ - formattedMessageBuffer_;
        // setup TUI and diagnose more UI errors here.
        // on success record the start time
        startTime_ = TimeStamper::now();
        if (!startTime_.isOk()) {
            return ResponseCode::SystemError;
        }
        error_ = ResponseCode::OK;
        return ResponseCode::OK;
    }

    void setHmacKey(const uint8_t (&key)[32]) { hmacKey_ = {key}; }

    void abort() {
        // tear down TUI here
        if (isPending()) {
            resultCB_->result(ResponseCode::Aborted, {}, {});
            error_ = ResponseCode::Ignored;
        }
    }

    void userCancel() {
        // tear down TUI here
        if (isPending()) error_ = ResponseCode::Canceled;
    }

    void finalize(const uint8_t key[32]) {
        if (error_ == ResponseCode::Ignored) return;
        resultCB_->result(error_, getMessage(), userConfirm(key));
        error_ = ResponseCode::Ignored;
        resultCB_ = {};
    }

    bool isPending() const { return error_ != ResponseCode::Ignored; }

    static Operation& get() {
        static Operation operation;
        return operation;
    }

    ResponseCode deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
        constexpr uint8_t testKeyByte = static_cast<uint8_t>(TestKeyBits::BYTE);
        constexpr uint8_t testKey[32] = {testKeyByte, testKeyByte, testKeyByte, testKeyByte,
                                         testKeyByte, testKeyByte, testKeyByte, testKeyByte,
                                         testKeyByte, testKeyByte, testKeyByte, testKeyByte,
                                         testKeyByte, testKeyByte, testKeyByte, testKeyByte};

        auto hmac = HMacer::hmac256(testKey, "\0", bytes_cast(secureInputToken.challenge),
                                    bytes_cast(secureInputToken.userId),
                                    bytes_cast(secureInputToken.authenticatorId),
                                    bytes_cast(hton(secureInputToken.authenticatorType)),
                                    bytes_cast(hton(secureInputToken.timestamp)));
        if (!hmac.isOk()) return ResponseCode::Unexpected;
        if (hmac.value() == secureInputToken.mac) {
            // okay so this is a test token
            switch (static_cast<TestModeCommands>(secureInputToken.challenge)) {
                case TestModeCommands::OK_EVENT: {
                    if (isPending()) {
                        finalize(testKey);
                        return ResponseCode::OK;
                    } else {
                        return ResponseCode::Ignored;
                    }
                }
                case TestModeCommands::CANCEL_EVENT: {
                    bool ignored = !isPending();
                    userCancel();
                    finalize(testKey);
                    return ignored ? ResponseCode::Ignored : ResponseCode::OK;
                }
                default:
                    return ResponseCode::Ignored;
            }
        }
        return ResponseCode::Ignored;
    }

   private:
    bool acceptAuthToken(const HardwareAuthToken&) { return false; }
    hidl_vec<uint8_t> getMessage() {
        hidl_vec<uint8_t> result;
        if (error_ != ResponseCode::OK) return {};
        result.setToExternal(formattedMessageBuffer_, formattedMessageLength_);
        return result;
    }
    hidl_vec<uint8_t> userConfirm(const uint8_t key[32]) {
        // tear down TUI here
        if (error_ != ResponseCode::OK) return {};
        confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage());
        if (!confirmationTokenScratchpad_.isOk()) {
            error_ = ResponseCode::Unexpected;
            return {};
        }
        hidl_vec<uint8_t> result;
        result.setToExternal(confirmationTokenScratchpad_->data(),
                             confirmationTokenScratchpad_->size());
        return result;
    }

    ResponseCode error_;
    uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)];
    size_t formattedMessageLength_;
    NullOr<array<uint8_t, 32>> confirmationTokenScratchpad_;
    Callback resultCB_;
    typename TimeStamper::TimeStamp startTime_;
    NullOr<array<uint8_t, 32>> hmacKey_;
};

}  // namespace
}  // namespace generic
}  // namespace V1_0
}  // namespace confirmationui
}  // namespace hardware
}  // namespace android

#endif  // CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
+335 −0
Original line number Diff line number Diff line
/*
**
** 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_1_0_DEFAULT_CBOR_H_
#define CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_

#include <stddef.h>
#include <stdint.h>
#include <type_traits>

namespace android {
namespace hardware {
namespace confirmationui {
namespace support {

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)> {
    typedef uint8_t type;
};
template <>
struct getUnsignedType<sizeof(uint16_t)> {
    typedef uint16_t type;
};
template <>
struct getUnsignedType<sizeof(uint32_t)> {
    typedef uint32_t type;
};
template <>
struct getUnsignedType<sizeof(uint64_t)> {
    typedef uint64_t type;
};

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 support
}  // namespace confirmationui
}  // namespace hardware
}  // namespace android

#endif  // CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_
+213 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading