blob: 12c20fae21bc04d9ae57bf11b5a93be3bd58c3f6 [file] [log] [blame]
Janis Danisevskis064ce852018-03-12 16:49:16 -07001/*
2**
3** Copyright 2018, 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 KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
19#define KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
20
21#include <android/hardware/confirmationui/1.0/types.h>
22#include <chrono>
23#include <stdint.h>
24#include <sys/types.h>
25#include <tuple>
26#include <unordered_map>
27
28namespace keystore {
29
30using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
31
32using std::chrono::time_point;
33using std::chrono::duration;
34
35template <typename Clock = std::chrono::steady_clock> class RateLimiting {
36 private:
37 struct Slot {
38 Slot() : previous_start{}, prompt_start{}, counter(0) {}
39 typename Clock::time_point previous_start;
40 typename Clock::time_point prompt_start;
41 uint32_t counter;
42 };
43
44 std::unordered_map<uid_t, Slot> slots_;
45
46 uint_t latest_requester_;
47
48 static std::chrono::seconds getBackoff(uint32_t counter) {
49 using namespace std::chrono_literals;
50 switch (counter) {
51 case 0:
52 case 1:
53 case 2:
54 return 0s;
55 case 3:
56 case 4:
57 case 5:
58 return 30s;
59 default:
60 return 60s * (1ULL << (counter - 6));
61 }
62 }
63
64 public:
65 // Exposes the number of used slots. This is only used by the test to verify the assumption
66 // about used counter slots.
67 size_t usedSlots() const { return slots_.size(); }
68 void doGC() {
69 using namespace std::chrono_literals;
70 using std::chrono::system_clock;
71 using std::chrono::time_point_cast;
72 auto then = Clock::now() - 24h;
73 auto iter = slots_.begin();
74 while (iter != slots_.end()) {
75 if (iter->second.prompt_start <= then) {
76 iter = slots_.erase(iter);
77 } else {
78 ++iter;
79 }
80 }
81 }
82
83 bool tryPrompt(uid_t id) {
84 using namespace std::chrono_literals;
85 // remove slots that have not been touched in 24 hours
86 doGC();
87 auto& slot = slots_[id];
88 auto now = Clock::now();
89 if (!slot.counter || slot.prompt_start <= now - getBackoff(slot.counter)) {
90 latest_requester_ = id;
91 slot.counter += 1;
92 slot.previous_start = slot.prompt_start;
93 slot.prompt_start = now;
94 return true;
95 }
96 return false;
97 }
98
99 void processResult(ConfirmationResponseCode rc) {
100 switch (rc) {
101 case ConfirmationResponseCode::OK:
102 // reset the counter slot
103 slots_.erase(latest_requester_);
104 return;
105 case ConfirmationResponseCode::Canceled:
106 // nothing to do here
107 return;
108 default:;
109 }
110
111 // roll back latest request
112 auto& slot = slots_[latest_requester_];
113 if (slot.counter <= 1) {
114 slots_.erase(latest_requester_);
115 return;
116 }
117 slot.counter -= 1;
118 slot.prompt_start = slot.previous_start;
119 }
120};
121
122} // namespace keystore
123
124#endif // KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_