summaryrefslogtreecommitdiffstats
path: root/keystore/auth_token_table.h
blob: 7c1836764c6f1e4824e4de61871f2203fdfdd5f2 (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
/*
 * Copyright (C) 2015 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 <memory>
#include <vector>

#include <hardware/hw_auth_token.h>
#include <keymaster/authorization_set.h>
#include <keymaster/key_blob.h>

#ifndef SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H
#define SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H

namespace keymaster {

namespace test {
class AuthTokenTableTest;
}  // namespace test

time_t clock_gettime_raw();

/**
 * AuthTokenTable manages a set of received authorization tokens and can provide the appropriate
 * token for authorizing a key operation.
 *
 * To keep the table from growing without bound, superseded entries are removed when possible, and
 * least recently used entries are automatically pruned when when the table exceeds a size limit,
 * which is expected to be relatively small, since the implementation uses a linear search.
 */
class AuthTokenTable {
  public:
    AuthTokenTable(size_t max_entries = 32, time_t (*clock_function)() = clock_gettime_raw)
        : max_entries_(max_entries), clock_function_(clock_function) {}

    enum Error {
        OK,
        AUTH_NOT_REQUIRED = -1,
        AUTH_TOKEN_EXPIRED = -2,    // Found a matching token, but it's too old.
        AUTH_TOKEN_WRONG_SID = -3,  // Found a token with the right challenge, but wrong SID.  This
                                    // most likely indicates that the authenticator was updated
                                    // (e.g. new fingerprint enrolled).
        OP_HANDLE_REQUIRED = -4,    // The key requires auth per use but op_handle was zero.
        AUTH_TOKEN_NOT_FOUND = -5,
        AUTH_BAD_PARAMS = -6,
    };

    /**
     * Add an authorization token to the table.  The table takes ownership of the argument.
     */
    void AddAuthenticationToken(const hw_auth_token_t* token);

    /**
     * Find an authorization token that authorizes the operation specified by \p operation_handle on
     * a key with the characteristics specified in \p key_info.
     *
     * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
     * and m is the number of entries in the table.  It could be made better, but n and m should
     * always be small.
     *
     * The table retains ownership of the returned object.
     */
    Error FindAuthorization(const AuthorizationSet& key_info,
                            keymaster_operation_handle_t op_handle, const hw_auth_token_t** found);

    /**
     * Find an authorization token that authorizes the operation specified by \p operation_handle on
     * a key with the characteristics specified in \p key_info.
     *
     * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
     * and m is the number of entries in the table.  It could be made better, but n and m should
     * always be small.
     *
     * The table retains ownership of the returned object.
     */
    Error FindAuthorization(const keymaster_key_param_t* params, size_t params_count,
                            keymaster_operation_handle_t op_handle, const hw_auth_token_t** found) {
        return FindAuthorization(AuthorizationSet(params, params_count), op_handle, found);
    }

    /**
     * Find an authorization token that authorizes the operation specified by \p handle on
     * a key with the characteristics specified in \p blob.
     *
     * The table retains ownership of the returned object.
     */
    Error FindAuthorization(const keymaster_key_blob_t& blob, keymaster_operation_handle_t handle,
                            const hw_auth_token_t** found) {
        KeyBlob key(blob);
        if (key.error()) {
            return AUTH_BAD_PARAMS;
        }
        AuthorizationSet auths(key.unenforced());
        for (auto param : key.enforced()) {
            auths.push_back(param);
        }
        return FindAuthorization(auths, handle, found);

    }


    /**
     * Mark operation completed.  This allows tokens associated with the specified operation to be
     * superseded by new tokens.
     */
    void MarkCompleted(const keymaster_operation_handle_t op_handle);

    size_t size() { return entries_.size(); }

  private:
    friend class AuthTokenTableTest;

    class Entry {
      public:
        Entry(const hw_auth_token_t* token, time_t current_time);
        Entry(Entry&& entry) { *this = std::move(entry); }

        void operator=(Entry&& rhs) {
            token_ = std::move(rhs.token_);
            time_received_ = rhs.time_received_;
            last_use_ = rhs.last_use_;
            operation_completed_ = rhs.operation_completed_;
        }

        bool operator<(const Entry& rhs) const { return last_use_ < rhs.last_use_; }

        void UpdateLastUse(time_t time);

        bool Supersedes(const Entry& entry) const;
        bool SatisfiesAuth(const std::vector<uint64_t>& sids, hw_authenticator_type_t auth_type);

        bool is_newer_than(const Entry* entry) {
            if (!entry)
                return true;
            return timestamp_host_order() > entry->timestamp_host_order();
        }

        void mark_completed() { operation_completed_ = true; }

        const hw_auth_token_t* token() { return token_.get(); }
        time_t time_received() const { return time_received_; }
        bool completed() const { return operation_completed_; }
        uint32_t timestamp_host_order() const;
        hw_authenticator_type_t authenticator_type() const;

      private:
        std::unique_ptr<const hw_auth_token_t> token_;
        time_t time_received_;
        time_t last_use_;
        bool operation_completed_;
    };

    Error FindAuthPerOpAuthorization(const std::vector<uint64_t>& sids,
                                     hw_authenticator_type_t auth_type,
                                     keymaster_operation_handle_t op_handle,
                                     const hw_auth_token_t** found);
    Error FindTimedAuthorization(const std::vector<uint64_t>& sids,
                                 hw_authenticator_type_t auth_type,
                                 const AuthorizationSet& key_info, const hw_auth_token_t** found);
    void ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids);
    void RemoveEntriesSupersededBy(const Entry& entry);
    bool IsSupersededBySomeEntry(const Entry& entry);

    std::vector<Entry> entries_;
    size_t max_entries_;
    time_t (*clock_function_)();
};

}  // namespace keymaster

#endif  // SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H