Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT auth: cache verifiers #5575

Merged
merged 3 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
};
}