summaryrefslogtreecommitdiff
path: root/trusty/confirmationui/NotSoSecureInput.cpp
blob: 18e45cd35bbdaeb45cf9dd5ddca0b4e8b502b650 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
 * 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 here
 * 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