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

Commit 8fe0cfb0 authored by Janis Danisevskis's avatar Janis Danisevskis
Browse files

First working version of the confirmationui HAL service

This implementation does not provide any security guaranties.
 * The input method (NotSoSecureInput) runs a crypto protocols that is
   sufficiently secure IFF the end point is implemented on a trustworthy
   secure input device. But since the endpoint is currently in the HAL
   service itself this implementation is not secure.
 * This implementation provides most of the functionality, but not the
   secure UI infrastructure required to run Android Protected
   Confirmation.

Bug: 146078942
Test: VtsHalConfirmationUIV1_0TargetTest
Change-Id: I14717b5fa4ef15db960cdd506b8c6fe5369aec8d
parent d8a8988c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
BreakBeforeBraces: Attach
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
IndentCaseLabels: false
ColumnLimit: 100
PointerBindsToType: true
SpacesBeforeTrailingComments: 2
+95 −0
Original line number Diff line number Diff line
// Copyright (C) 2020 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.
//

// WARNING: Everything listed here will be built on ALL platforms,
// including x86, the emulator, and the SDK.  Modules must be uniquely
// named (liblights.panda), and must build everywhere, or limit themselves
// to only building on ARM if they include assembly. Individual makefiles
// are responsible for having their own logic, for fine-grained control.

cc_binary {
    name: "android.hardware.confirmationui@1.0-service.trusty",
    relative_install_path: "hw",
    vendor: true,
    shared_libs: [
        "android.hardware.confirmationui@1.0",
        "android.hardware.confirmationui.not-so-secure-input",
        "android.hardware.confirmationui@1.0-lib.trusty",
        "libbase",
        "libhidlbase",
        "libutils",
    ],

    init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"],

    vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"],

    srcs: [
        "service.cpp",
    ],

    cflags: [
        "-Wall",
        "-Werror",
        "-DTEEUI_USE_STD_VECTOR",
    ],
}

cc_library {
    name: "android.hardware.confirmationui@1.0-lib.trusty",
    vendor: true,
    shared_libs: [
        "android.hardware.confirmationui@1.0",
        "android.hardware.keymaster@4.0",
        "libbase",
        "libhidlbase",
        "libteeui_hal_support",
        "libtrusty",
        "libutils",
    ],

    export_include_dirs: ["include"],

    srcs: [
        "TrustyApp.cpp",
        "TrustyConfirmationUI.cpp",
    ],

    cflags: [
        "-Wall",
        "-Werror",
        "-DTEEUI_USE_STD_VECTOR",
    ],
}

cc_library {
    name: "android.hardware.confirmationui.not-so-secure-input",
    vendor: true,
    shared_libs: [
        "libbase",
        "libcrypto",
        "libteeui_hal_support",
    ],

    srcs: [
        "NotSoSecureInput.cpp",
    ],

    cflags: [
        "-Wall",
        "-Werror",
        "-DTEEUI_USE_STD_VECTOR",
    ],
}
 No newline at end of file
+207 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020, 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.
 */

#include <android-base/logging.h>
#include <endian.h>
#include <memory>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <secure_input/evdev.h>
#include <secure_input/secure_input_device.h>
#include <teeui/utils.h>

#include <initializer_list>

using namespace secure_input;

using teeui::AuthTokenKey;
using teeui::ByteBufferProxy;
using teeui::Hmac;
using teeui::optional;
using teeui::ResponseCode;
using teeui::TestKeyBits;

constexpr const auto kTestKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE));

class SecureInputHMacer {
  public:
    static optional<Hmac> hmac256(const AuthTokenKey& key,
                                  std::initializer_list<ByteBufferProxy> buffers) {
        HMAC_CTX hmacCtx;
        HMAC_CTX_init(&hmacCtx);
        if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) {
            return {};
        }
        for (auto& buffer : buffers) {
            if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
                return {};
            }
        }
        Hmac result;
        if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
            return {};
        }
        return result;
    }
};

using HMac = teeui::HMac<SecureInputHMacer>;

Nonce generateNonce() {
    /*
     * Completely random nonce.
     * Running the secure input protocol from the HAL service is not secure
     * because we don't trust the non-secure world (i.e., HLOS/Android/Linux). So
     * using a constant "nonce" here does not weaken security. If this code runs
     * on a truly trustworthy source of input events this function needs to return
     * hight entropy nonces.
     * As of this writing the call to RAND_bytes is commented, because the
     * emulator this HAL service runs on does not have a good source of entropy.
     * It would block the call to RAND_bytes indefinitely.
     */
    Nonce result{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
    // RAND_bytes(result.data(), result.size());
    return result;
}

/**
 * This is an implementation of the SecureInput protocol in unserspace. This is
 * just an example and should not be used as is. The protocol implemented her
 * should be used by a trusted input device that can assert user events with
 * high assurance even if the HLOS kernel is compromised. A confirmationui HAL
 * that links directly against this implementation is not secure and shal not be
 * used on a production device.
 */
class NotSoSecureInput : public SecureInput {
  public:
    NotSoSecureInput(HsBeginCb hsBeginCb, HsFinalizeCb hsFinalizeCb, DeliverEventCb deliverEventCb,
                     InputResultCb inputResultCb)
        : hsBeginCb_{hsBeginCb}, hsFinalizeCb_{hsFinalizeCb}, deliverEventCb_{deliverEventCb},
          inputResultCb_{inputResultCb}, discardEvents_{true} {}

    operator bool() const override { return true; }

    void handleEvent(const EventDev& evdev) override {
        bool gotEvent;
        input_event evt;
        std::tie(gotEvent, evt) = evdev.readEvent();
        while (gotEvent) {
            if (!(discardEvents_) && evt.type == EV_KEY &&
                (evt.code == KEY_POWER || evt.code == KEY_VOLUMEDOWN || evt.code == KEY_VOLUMEUP) &&
                evt.value == 1) {
                DTupKeyEvent event = DTupKeyEvent::RESERVED;

                // Translate the event code into DTupKeyEvent which the TA understands.
                switch (evt.code) {
                case KEY_POWER:
                    event = DTupKeyEvent::PWR;
                    break;
                case KEY_VOLUMEDOWN:
                    event = DTupKeyEvent::VOL_DOWN;
                    break;
                case KEY_VOLUMEUP:
                    event = DTupKeyEvent::VOL_UP;
                    break;
                }

                // The event goes into the HMAC in network byte order.
                uint32_t keyEventBE = htobe32(static_cast<uint32_t>(event));
                auto signature = HMac::hmac256(kTestKey, kConfirmationUIEventLabel,
                                               teeui::bytesCast(keyEventBE), nCi_);

                teeui::ResponseCode rc;
                InputResponse ir;
                auto response = std::tie(rc, ir);
                if (event != DTupKeyEvent::RESERVED) {
                    response = deliverEventCb_(event, *signature);
                    if (rc != ResponseCode::OK) {
                        LOG(ERROR) << "DeliverInputEvent returned with " << uint32_t(rc);
                        inputResultCb_(rc);
                    } else {
                        switch (ir) {
                        case InputResponse::OK:
                            inputResultCb_(rc);
                            break;
                        case InputResponse::PENDING_MORE:
                            rc = performDTUPHandshake();
                            if (rc != ResponseCode::OK) {
                                inputResultCb_(rc);
                            }
                            break;
                        case InputResponse::TIMED_OUT:
                            inputResultCb_(rc);
                            break;
                        }
                    }
                }
            }
            std::tie(gotEvent, evt) = evdev.readEvent();
        }
    }

    void start() override {
        auto rc = performDTUPHandshake();
        if (rc != ResponseCode::OK) {
            inputResultCb_(rc);
        }
        discardEvents_ = false;
    };

  private:
    teeui::ResponseCode performDTUPHandshake() {
        ResponseCode rc;
        LOG(INFO) << "Start handshake";
        Nonce nCo;
        std::tie(rc, nCo) = hsBeginCb_();
        if (rc != ResponseCode::OK) {
            LOG(ERROR) << "Failed to begin secure input handshake (" << uint32_t(rc) << ")";
            return rc;
        }

        nCi_ = generateNonce();
        rc =
            hsFinalizeCb_(*HMac::hmac256(kTestKey, kConfirmationUIHandshakeLabel, nCo, nCi_), nCi_);

        if (rc != ResponseCode::OK) {
            LOG(ERROR) << "Failed to finalize secure input handshake (" << uint32_t(rc) << ")";
            return rc;
        }
        return ResponseCode::OK;
    }

    HsBeginCb hsBeginCb_;
    HsFinalizeCb hsFinalizeCb_;
    DeliverEventCb deliverEventCb_;
    InputResultCb inputResultCb_;

    std::atomic_bool discardEvents_;
    Nonce nCi_;
};

namespace secure_input {

std::shared_ptr<SecureInput> createSecureInput(SecureInput::HsBeginCb hsBeginCb,
                                               SecureInput::HsFinalizeCb hsFinalizeCb,
                                               SecureInput::DeliverEventCb deliverEventCb,
                                               SecureInput::InputResultCb inputResultCb) {
    return std::make_shared<NotSoSecureInput>(hsBeginCb, hsFinalizeCb, deliverEventCb,
                                              inputResultCb);
}

}  // namespace secure_input
+20 −0
Original line number Diff line number Diff line
## Secure UI Architecture

To implement confirmationui a secure UI architecture is required. This entails a way
to display the confirmation dialog driven by a reduced trusted computing base, typically
a trusted execution environment (TEE), without having to rely on Linux and the Android
system for integrity and authenticity of input events. This implementation provides
neither. But it provides most of the functionlity required to run a full Android Protected
Confirmation feature when integrated into a secure UI architecture.

## Secure input (NotSoSecureInput)

This implementation does not provide any security guaranties.
The input method (NotSoSecureInput) runs a cryptographic protocols that is
sufficiently secure IFF the end point is implemented on a trustworthy
secure input device. But since the endpoint is currently in the HAL
service itself this implementation is not secure.

NOTE that a secure input device end point needs a good source of entropy
for generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce)
uses a constant nonce.
 No newline at end of file
+156 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020, 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.
 */

#include "TrustyApp.h"

#include <android-base/logging.h>
#include <sys/uio.h>
#include <trusty/tipc.h>

namespace android {
namespace trusty {

// 0x1000 is the message buffer size but we need to leave some space for a protocol header.
// This assures that packets can always be read/written in one read/write operation.
static constexpr const uint32_t kPacketSize = 0x1000 - 32;

enum class PacketType : uint32_t {
    SND,
    RCV,
    ACK,
};

struct PacketHeader {
    PacketType type;
    uint32_t remaining;
};

const char* toString(PacketType t) {
    switch (t) {
    case PacketType::SND:
        return "SND";
    case PacketType::RCV:
        return "RCV";
    case PacketType::ACK:
        return "ACK";
    default:
        return "UNKNOWN";
    }
}

static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader);
static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize;

ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
                  uint8_t* iend) {
    while (obegin != oend) {
        PacketHeader header = {
            .type = PacketType::SND,
            .remaining = uint32_t(oend - obegin),
        };
        uint32_t body_size = std::min(kPayloadSize, header.remaining);
        iovec iov[] = {
            {
                .iov_base = &header,
                .iov_len = kHeaderSize,
            },
            {
                .iov_base = const_cast<uint8_t*>(obegin),
                .iov_len = body_size,
            },
        };
        int rc = writev(handle, iov, 2);
        if (!rc) {
            PLOG(ERROR) << "Error sending SND message. " << rc;
            return rc;
        }

        obegin += body_size;

        rc = read(handle, &header, kHeaderSize);
        if (!rc) {
            PLOG(ERROR) << "Error reading ACK. " << rc;
            return rc;
        }

        if (header.type != PacketType::ACK || header.remaining != oend - obegin) {
            LOG(ERROR) << "malformed ACK";
            return -1;
        }
    }

    ssize_t remaining = 0;
    auto begin = ibegin;
    do {
        PacketHeader header = {
            .type = PacketType::RCV,
            .remaining = 0,
        };

        iovec iov[] = {
            {
                .iov_base = &header,
                .iov_len = kHeaderSize,
            },
            {
                .iov_base = begin,
                .iov_len = uint32_t(iend - begin),
            },
        };

        ssize_t rc = writev(handle, iov, 1);
        if (!rc) {
            PLOG(ERROR) << "Error sending RCV message. " << rc;
            return rc;
        }

        rc = readv(handle, iov, 2);
        if (rc < 0) {
            PLOG(ERROR) << "Error reading response. " << rc;
            return rc;
        }

        uint32_t body_size = std::min(kPayloadSize, header.remaining);
        if (body_size != rc - kHeaderSize) {
            LOG(ERROR) << "Unexpected amount of data: " << rc;
            return -1;
        }

        remaining = header.remaining - body_size;
        begin += body_size;
    } while (remaining);

    return begin - ibegin;
}

TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
    : handle_(kInvalidHandle) {
    handle_ = tipc_connect(path.c_str(), appname.c_str());
    if (handle_ == kInvalidHandle) {
        LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
                   << "\"" << path << "\"";
    }
    LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
}
TrustyApp::~TrustyApp() {
    if (handle_ != kInvalidHandle) {
        tipc_close(handle_);
    }
    LOG(INFO) << "Done shutting down TrustyApp";
}

}  // namespace trusty
}  // namespace android
Loading