// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "BearerTokens.h" #include namespace { // Every byte is examined even after the first mismatch, so the comparison time does not // reveal how long the matching prefix was. bool ConstantTimeEqual(const std::string &a, const std::string &b) { if (a.size() != b.size()) return false; volatile unsigned char diff = 0; for (size_t i = 0; i < a.size(); i++) diff |= static_cast(a[i] ^ b[i]); return diff == 0; } // RFC 6750: the scheme name is case-insensitive and is followed by one or more spaces and the // token. Returns the token, or nothing when the header is not a bearer credential. std::optional BearerToken(const std::string &header) { if (header.size() < 7) return std::nullopt; std::string scheme = header.substr(0, 6); for (auto &c: scheme) c = static_cast(std::tolower(static_cast(c))); if (scheme != "bearer" || header[6] != ' ') return std::nullopt; size_t start = header.find_first_not_of(' ', 6); if (start == std::string::npos) return std::nullopt; size_t end = header.find_last_not_of(" \t\r\n"); return header.substr(start, end - start + 1); } } void BearerTokens::Replace(std::vector input) { std::unique_lock ul(m); tokens.clear(); for (auto &t: input) if (!t.empty()) tokens.push_back(std::move(t)); } bool BearerTokens::Accept(const std::optional &authorization) const { std::unique_lock ul(m); if (tokens.empty()) return true; if (!authorization) return false; auto presented = BearerToken(*authorization); if (!presented) return false; for (const auto &t: tokens) if (ConstantTimeEqual(t, *presented)) return true; return false; }