confirmationui_rate_limiting.h 3.6 KB

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