#pragma once #include #include #include // Who may use the Debug Console (ADR 0010): a token that only the device and its owner know, proved // with a challenge and an answer so that it never crosses the network, and a pause after wrong answers. namespace roro::debug { constexpr size_t kTokenChars = 20; // a token the device makes: 100 bits constexpr size_t kTokenRandom = 13; // the random bytes it takes constexpr size_t kMinTokenChars = 16; // a token typed by hand, once tidied constexpr size_t kMaxTokenChars = 64; constexpr size_t kNonceBytes = 16; // A token from random bytes: 20 characters of Crockford's base32 (no I, L, O or U to misread). std::string makeToken(const uint8_t random[kTokenRandom]); // What a person typed, as it's stored and compared: no dashes or spaces, in capitals, and with O // read as 0, I and L as 1. So a token can be read off the screen in groups, typed in any case, // and the usual misreadings don't matter. std::string tidyToken(const std::string& typed); // A tidied token that may be stored: 16 to 64 printable ASCII characters, as tidyToken leaves them. bool validToken(const std::string& tidied); // For the screen: K7QF-3M2X-9WBD-HT4P-6RNC. std::string groupToken(const std::string& token); void hmacSha256(const uint8_t* key, size_t keyLen, const uint8_t* message, size_t messageLen, uint8_t out[32]); std::string toHex(const uint8_t* data, size_t len); // What a client must send back for a challenge: HMAC-SHA256 of the nonce, keyed by the token, in hex. std::string answerFor(const std::string& token, const uint8_t nonce[kNonceBytes]); // Compares without stopping at the first difference, so timing says nothing about the answer. bool sameText(const std::string& a, const std::string& b); // Five wrong answers in a row, from anyone, and nobody is listened to for a minute. class AuthGate { public: static constexpr int kMaxFailures = 5; static constexpr uint32_t kLockMs = 60000; bool locked(uint32_t nowMs); // A wrong answer. True if it's the one that starts the pause. bool failed(uint32_t nowMs); void succeeded() { failures_ = 0; } int failures() const { return failures_; } private: int failures_ = 0; bool locked_ = false; uint32_t lockedAtMs_ = 0; }; } // namespace roro::debug