diff --git a/.daily_canary b/.daily_canary index 21f88a729b7e..203272dcaea1 100644 --- a/.daily_canary +++ b/.daily_canary @@ -1,4 +1,4 @@ --- ___ ___ (- -) (= =) | Y & +--? ( V ) \ . \ O +---=---' -/--x-m- /--n-n---xXx--/--yY-----. +/--x-m- /--n-n---xXx--/--yY-----] diff --git a/.threading_canary b/.threading_canary index 40d41071089a..9cf8ff86c29f 100644 --- a/.threading_canary +++ b/.threading_canary @@ -1 +1 @@ -THIS looks like a job for Threading Canard!y!1!.. +THIS looks like a job for threading Canard!y!1!.. diff --git a/include/ccf/endpoints/authentication/jwt_auth.h b/include/ccf/endpoints/authentication/jwt_auth.h index d1ff334a8072..fd12b389fab4 100644 --- a/include/ccf/endpoints/authentication/jwt_auth.h +++ b/include/ccf/endpoints/authentication/jwt_auth.h @@ -17,14 +17,20 @@ namespace ccf nlohmann::json payload; }; + struct VerifiersCache; + class JwtAuthnPolicy : public AuthnPolicy { protected: static const OpenAPISecuritySchema security_schema; + std::unique_ptr verifiers; public: static constexpr auto SECURITY_SCHEME_NAME = "jwt"; + JwtAuthnPolicy(); + virtual ~JwtAuthnPolicy(); + std::unique_ptr authenticate( kv::ReadOnlyTx& tx, const std::shared_ptr& ctx, diff --git a/src/endpoints/authentication/jwt_auth.cpp b/src/endpoints/authentication/jwt_auth.cpp index 3c70966d36d7..ef17ffb9b0bc 100644 --- a/src/endpoints/authentication/jwt_auth.cpp +++ b/src/endpoints/authentication/jwt_auth.cpp @@ -3,13 +3,47 @@ #include "ccf/endpoints/authentication/jwt_auth.h" +#include "ccf/pal/locking.h" #include "ccf/rpc_context.h" #include "ccf/service/tables/jwt.h" +#include "ds/lru.h" #include "enclave/enclave_time.h" #include "http/http_jwt.h" namespace ccf { + struct VerifiersCache + { + static constexpr size_t DEFAULT_MAX_VERIFIERS = 10; + + using DER = std::vector; + ccf::pal::Mutex verifiers_lock; + LRU verifiers; + + VerifiersCache(size_t max_verifiers = DEFAULT_MAX_VERIFIERS) : + verifiers(max_verifiers) + {} + + crypto::VerifierPtr get_verifier(const DER& der) + { + std::lock_guard guard(verifiers_lock); + + auto it = verifiers.find(der); + if (it == verifiers.end()) + { + it = verifiers.insert(der, crypto::make_unique_verifier(der)); + } + + return it->second; + } + }; + + JwtAuthnPolicy::JwtAuthnPolicy() : + verifiers(std::make_unique()) + {} + + JwtAuthnPolicy::~JwtAuthnPolicy() = default; + std::unique_ptr JwtAuthnPolicy::authenticate( kv::ReadOnlyTx& tx, const std::shared_ptr& ctx, @@ -29,43 +63,47 @@ namespace ccf ccf::Tables::JWT_PUBLIC_SIGNING_KEY_ISSUER); const auto key_id = token.header_typed.kid; const auto token_key = keys->get(key_id); + if (!token_key.has_value()) { error_reason = "JWT signing key not found"; } - else if (!http::JwtVerifier::validate_token_signature( - token, token_key.value())) - { - error_reason = "JWT signature is invalid"; - } else { - // Check that the Not Before and Expiration Time claims are valid - const size_t time_now = - std::chrono::duration_cast( - ccf::get_enclave_time()) - .count(); - if (time_now < token.payload_typed.nbf) - { - error_reason = fmt::format( - "Current time {} is before token's Not Before (nbf) claim {}", - time_now, - token.payload_typed.nbf); - } - else if (time_now > token.payload_typed.exp) + auto verifier = verifiers->get_verifier(token_key.value()); + if (!http::JwtVerifier::validate_token_signature(token, verifier)) { - error_reason = fmt::format( - "Current time {} is after token's Expiration Time (exp) claim {}", - time_now, - token.payload_typed.exp); + error_reason = "JWT signature is invalid"; } else { - auto identity = std::make_unique(); - identity->key_issuer = key_issuers->get(key_id).value(); - identity->header = std::move(token.header); - identity->payload = std::move(token.payload); - return identity; + // Check that the Not Before and Expiration Time claims are valid + const size_t time_now = + std::chrono::duration_cast( + ccf::get_enclave_time()) + .count(); + if (time_now < token.payload_typed.nbf) + { + error_reason = fmt::format( + "Current time {} is before token's Not Before (nbf) claim {}", + time_now, + token.payload_typed.nbf); + } + else if (time_now > token.payload_typed.exp) + { + error_reason = fmt::format( + "Current time {} is after token's Expiration Time (exp) claim {}", + time_now, + token.payload_typed.exp); + } + else + { + auto identity = std::make_unique(); + identity->key_issuer = key_issuers->get(key_id).value(); + identity->header = std::move(token.header); + identity->payload = std::move(token.payload); + return identity; + } } } } diff --git a/src/http/http_jwt.h b/src/http/http_jwt.h index e300214105e1..64e731d3fa05 100644 --- a/src/http/http_jwt.h +++ b/src/http/http_jwt.h @@ -171,16 +171,14 @@ namespace http } static bool validate_token_signature( - const Token& token, std::vector cert_der) + const Token& token, const crypto::VerifierPtr& verifier) { - auto verifier = crypto::make_unique_verifier(cert_der); - bool valid = verifier->verify( + return verifier->verify( (uint8_t*)token.signed_content.data(), token.signed_content.size(), token.signature.data(), token.signature.size(), crypto::MDType::SHA256); - return valid; } }; }