• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <gtest/gtest.h>
18 
19 #include "../confirmationui_rate_limiting.h"
20 #include <keymaster/logger.h>
21 
22 using std::vector;
23 
24 namespace keystore {
25 
26 namespace test {
27 
28 namespace {
29 
30 class StdoutLogger : public ::keymaster::Logger {
31   public:
StdoutLogger()32     StdoutLogger() { set_instance(this); }
33 
log_msg(LogLevel level,const char * fmt,va_list args) const34     int log_msg(LogLevel level, const char* fmt, va_list args) const {
35         int output_len = 0;
36         switch (level) {
37         case DEBUG_LVL:
38             output_len = printf("DEBUG: ");
39             break;
40         case INFO_LVL:
41             output_len = printf("INFO: ");
42             break;
43         case WARNING_LVL:
44             output_len = printf("WARNING: ");
45             break;
46         case ERROR_LVL:
47             output_len = printf("ERROR: ");
48             break;
49         case SEVERE_LVL:
50             output_len = printf("SEVERE: ");
51             break;
52         }
53 
54         output_len += vprintf(fmt, args);
55         output_len += printf("\n");
56         return output_len;
57     }
58 };
59 
60 StdoutLogger logger;
61 
62 class FakeClock : public std::chrono::steady_clock {
63   private:
64     static time_point sNow;
65 
66   public:
setNow(time_point newNow)67     static void setNow(time_point newNow) { sNow = newNow; }
now()68     static time_point now() noexcept { return sNow; }
69 };
70 
71 FakeClock::time_point FakeClock::sNow;
72 
73 }  // namespace
74 
75 /*
76  * Test that there are no residual slots when various apps receive successful confirmations.
77  */
TEST(ConfirmationUIRateLimitingTest,noPenaltyTest)78 TEST(ConfirmationUIRateLimitingTest, noPenaltyTest) {
79     auto now = std::chrono::steady_clock::now();
80     RateLimiting<FakeClock> rateLimiting;
81     FakeClock::setNow(now);
82 
83     for (int i = 0; i < 10000; ++i) {
84         ASSERT_TRUE(rateLimiting.tryPrompt(rand()));
85         rateLimiting.processResult(ConfirmationResponseCode::OK);
86     }
87 
88     ASSERT_EQ(0U, rateLimiting.usedSlots());
89 }
90 
TEST(ConfirmationUIRateLimitingTest,policyTest)91 TEST(ConfirmationUIRateLimitingTest, policyTest) {
92     using namespace std::chrono_literals;
93     auto now = std::chrono::steady_clock::now();
94     RateLimiting<FakeClock> rateLimiting;
95     FakeClock::setNow(now);
96 
97     // first three tries are free
98     for (int i = 0; i < 3; ++i) {
99         ASSERT_TRUE(rateLimiting.tryPrompt(20));
100         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
101     }
102 
103     // throw in a couple of successful confirmations by other apps to make sure there
104     // is not cross talk
105     for (int i = 0; i < 10000; ++i) {
106         uid_t id = rand();
107         if (id == 20) continue;
108         ASSERT_TRUE(rateLimiting.tryPrompt(id));
109         rateLimiting.processResult(ConfirmationResponseCode::OK);
110     }
111 
112     // the next three tries get a 30s penalty
113     for (int i = 3; i < 6; ++i) {
114         FakeClock::setNow(FakeClock::now() + 29s);
115         ASSERT_FALSE(rateLimiting.tryPrompt(20));
116         FakeClock::setNow(FakeClock::now() + 1s);
117         ASSERT_TRUE(rateLimiting.tryPrompt(20));
118         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
119     }
120 
121     // throw in a couple of successful confirmations by other apps to make sure there
122     // is not cross talk
123     for (int i = 0; i < 10000; ++i) {
124         uid_t id = rand();
125         if (id == 20) continue;
126         ASSERT_TRUE(rateLimiting.tryPrompt(id));
127         rateLimiting.processResult(ConfirmationResponseCode::OK);
128     }
129 
130     // there after the penalty doubles with each cancellation
131     for (int i = 6; i < 17; ++i) {
132         FakeClock::setNow((FakeClock::now() + 60s * (1ULL << (i - 6))) - 1s);
133         ASSERT_FALSE(rateLimiting.tryPrompt(20));
134         FakeClock::setNow(FakeClock::now() + 1s);
135         ASSERT_TRUE(rateLimiting.tryPrompt(20));
136         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
137     }
138 
139     // throw in a couple of successful confirmations by other apps to make sure there
140     // is not cross talk
141     for (int i = 0; i < 10000; ++i) {
142         uid_t id = rand();
143         if (id == 20) continue;
144         ASSERT_TRUE(rateLimiting.tryPrompt(id));
145         rateLimiting.processResult(ConfirmationResponseCode::OK);
146     }
147 
148     ASSERT_EQ(1U, rateLimiting.usedSlots());
149 
150     FakeClock::setNow(FakeClock::now() + 24h - 1s);
151     ASSERT_FALSE(rateLimiting.tryPrompt(20));
152 
153     // after 24h the counter is forgotten
154     FakeClock::setNow(FakeClock::now() + 1s);
155     ASSERT_TRUE(rateLimiting.tryPrompt(20));
156     rateLimiting.processResult(ConfirmationResponseCode::Canceled);
157 
158     // throw in a couple of successful confirmations by other apps to make sure there
159     // is not cross talk
160     for (int i = 0; i < 10000; ++i) {
161         uid_t id = rand();
162         if (id == 20) continue;
163         ASSERT_TRUE(rateLimiting.tryPrompt(id));
164         rateLimiting.processResult(ConfirmationResponseCode::OK);
165     }
166 
167     for (int i = 1; i < 3; ++i) {
168         ASSERT_TRUE(rateLimiting.tryPrompt(20));
169         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
170     }
171 
172     // throw in a couple of successful confirmations by other apps to make sure there
173     // is not cross talk
174     for (int i = 0; i < 10000; ++i) {
175         uid_t id = rand();
176         if (id == 20) continue;
177         ASSERT_TRUE(rateLimiting.tryPrompt(id));
178         rateLimiting.processResult(ConfirmationResponseCode::OK);
179     }
180 
181     for (int i = 3; i < 6; ++i) {
182         FakeClock::setNow(FakeClock::now() + 29s);
183         ASSERT_FALSE(rateLimiting.tryPrompt(20));
184         FakeClock::setNow(FakeClock::now() + 1s);
185         ASSERT_TRUE(rateLimiting.tryPrompt(20));
186         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
187     }
188 
189     // throw in a couple of successful confirmations by other apps to make sure there
190     // is not cross talk
191     for (int i = 0; i < 10000; ++i) {
192         uid_t id = rand();
193         if (id == 20) continue;
194         ASSERT_TRUE(rateLimiting.tryPrompt(id));
195         rateLimiting.processResult(ConfirmationResponseCode::OK);
196     }
197 
198     for (int i = 6; i < 17; ++i) {
199         FakeClock::setNow((FakeClock::now() + 60s * (1ULL << (i - 6))) - 1s);
200         ASSERT_FALSE(rateLimiting.tryPrompt(20));
201         FakeClock::setNow(FakeClock::now() + 1s);
202         ASSERT_TRUE(rateLimiting.tryPrompt(20));
203         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
204     }
205 
206     // throw in a couple of successful confirmations by other apps to make sure there
207     // is not cross talk
208     for (int i = 0; i < 10000; ++i) {
209         uid_t id = rand();
210         if (id == 20) continue;
211         ASSERT_TRUE(rateLimiting.tryPrompt(id));
212         rateLimiting.processResult(ConfirmationResponseCode::OK);
213     }
214 
215     ASSERT_EQ(1U, rateLimiting.usedSlots());
216 }
217 
TEST(ConfirmationUIRateLimitingTest,rewindTest)218 TEST(ConfirmationUIRateLimitingTest, rewindTest) {
219     using namespace std::chrono_literals;
220     auto now = std::chrono::steady_clock::now();
221     RateLimiting<FakeClock> rateLimiting;
222 
223     // first three tries are free
224     for (int i = 0; i < 3; ++i) {
225         FakeClock::setNow(now);
226         ASSERT_TRUE(rateLimiting.tryPrompt(20));
227         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
228     }
229 
230     for (int i = 3; i < 6; ++i) {
231         FakeClock::setNow(FakeClock::now() + 29s);
232         ASSERT_FALSE(rateLimiting.tryPrompt(20));
233         FakeClock::setNow(FakeClock::now() + 1s);
234         ASSERT_TRUE(rateLimiting.tryPrompt(20));
235         rateLimiting.processResult(ConfirmationResponseCode::Canceled);
236     }
237 
238     FakeClock::setNow(FakeClock::now() + 59s);
239     ASSERT_FALSE(rateLimiting.tryPrompt(20));
240     FakeClock::setNow(FakeClock::now() + 1s);
241     ASSERT_TRUE(rateLimiting.tryPrompt(20));
242     rateLimiting.processResult(ConfirmationResponseCode::Aborted);
243 
244     FakeClock::setNow(FakeClock::now() - 1s);
245     ASSERT_FALSE(rateLimiting.tryPrompt(20));
246     FakeClock::setNow(FakeClock::now() + 1s);
247     ASSERT_TRUE(rateLimiting.tryPrompt(20));
248     rateLimiting.processResult(ConfirmationResponseCode::SystemError);
249 
250     // throw in a couple of successful confirmations by other apps to make sure there
251     // is not cross talk
252     for (int i = 0; i < 10000; ++i) {
253         uid_t id = rand();
254         if (id == 20) continue;
255         ASSERT_TRUE(rateLimiting.tryPrompt(id));
256         rateLimiting.processResult(ConfirmationResponseCode::OK);
257     }
258 
259     FakeClock::setNow(FakeClock::now() - 1s);
260     ASSERT_FALSE(rateLimiting.tryPrompt(20));
261     FakeClock::setNow(FakeClock::now() + 1s);
262     ASSERT_TRUE(rateLimiting.tryPrompt(20));
263     rateLimiting.processResult(ConfirmationResponseCode::UIError);
264 
265     FakeClock::setNow(FakeClock::now() - 1s);
266     ASSERT_FALSE(rateLimiting.tryPrompt(20));
267     FakeClock::setNow(FakeClock::now() + 1s);
268     ASSERT_TRUE(rateLimiting.tryPrompt(20));
269 
270     ASSERT_EQ(1U, rateLimiting.usedSlots());
271 }
272 
273 }  // namespace test
274 }  // namespace keystore
275