Skip to content

Commit

Permalink
JWT auth: cache verifiers (#5575)
Browse files Browse the repository at this point in the history
  • Loading branch information
jumaffre authored Aug 21, 2023
1 parent 417631d commit 054356b
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .daily_canary
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
--- ___ ___
(- -) (= =) | Y & +--?
( V ) \ . \ O +---=---'
/--x-m- /--n-n---xXx--/--yY-----.
/--x-m- /--n-n---xXx--/--yY-----]
2 changes: 1 addition & 1 deletion .threading_canary
Original file line number Diff line number Diff line change
@@ -1 +1 @@
THIS looks like a job for Threading Canard!y!1!..
THIS looks like a job for threading Canard!y!1!..
6 changes: 6 additions & 0 deletions include/ccf/endpoints/authentication/jwt_auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@ namespace ccf
nlohmann::json payload;
};

struct VerifiersCache;

class JwtAuthnPolicy : public AuthnPolicy
{
protected:
static const OpenAPISecuritySchema security_schema;
std::unique_ptr<VerifiersCache> verifiers;

public:
static constexpr auto SECURITY_SCHEME_NAME = "jwt";

JwtAuthnPolicy();
virtual ~JwtAuthnPolicy();

std::unique_ptr<AuthnIdentity> authenticate(
kv::ReadOnlyTx& tx,
const std::shared_ptr<ccf::RpcContext>& ctx,
Expand Down
92 changes: 65 additions & 27 deletions src/endpoints/authentication/jwt_auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t>;
ccf::pal::Mutex verifiers_lock;
LRU<DER, crypto::VerifierPtr> verifiers;

VerifiersCache(size_t max_verifiers = DEFAULT_MAX_VERIFIERS) :
verifiers(max_verifiers)
{}

crypto::VerifierPtr get_verifier(const DER& der)
{
std::lock_guard<ccf::pal::Mutex> 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<VerifiersCache>())
{}

JwtAuthnPolicy::~JwtAuthnPolicy() = default;

std::unique_ptr<AuthnIdentity> JwtAuthnPolicy::authenticate(
kv::ReadOnlyTx& tx,
const std::shared_ptr<ccf::RpcContext>& ctx,
Expand All @@ -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<std::chrono::seconds>(
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<JwtAuthnIdentity>();
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<std::chrono::seconds>(
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<JwtAuthnIdentity>();
identity->key_issuer = key_issuers->get(key_id).value();
identity->header = std::move(token.header);
identity->payload = std::move(token.payload);
return identity;
}
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/http/http_jwt.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,14 @@ namespace http
}

static bool validate_token_signature(
const Token& token, std::vector<uint8_t> 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;
}
};
}

0 comments on commit 054356b

Please sign in to comment.