in src/node/jwt.h [132:275]
static bool set_jwt_public_signing_keys(
kv::Tx& tx,
const ProposalId& proposal_id,
std::string issuer,
const JwtIssuerMetadata& issuer_metadata,
const JsonWebKeySet& jwks)
{
auto keys = tx.rw<JwtPublicSigningKeys>(Tables::JWT_PUBLIC_SIGNING_KEYS);
auto key_issuer =
tx.rw<JwtPublicSigningKeyIssuer>(Tables::JWT_PUBLIC_SIGNING_KEY_ISSUER);
auto log_prefix = proposal_id.empty() ?
"JWT key auto-refresh" :
fmt::format("Proposal {}", proposal_id);
// add keys
if (jwks.keys.empty())
{
LOG_FAIL_FMT("{}: JWKS has no keys", log_prefix, proposal_id);
return false;
}
std::map<std::string, std::vector<uint8_t>> new_keys;
for (auto& jwk : jwks.keys)
{
if (keys->has(jwk.kid) && key_issuer->get(jwk.kid).value() != issuer)
{
LOG_FAIL_FMT(
"{}: key id {} already added for different issuer",
log_prefix,
jwk.kid);
return false;
}
if (jwk.x5c.empty())
{
LOG_FAIL_FMT("{}: JWKS is invalid (empty x5c)", log_prefix);
return false;
}
auto& der_base64 = jwk.x5c[0];
ccf::Cert der;
try
{
der = crypto::raw_from_b64(der_base64);
}
catch (const std::invalid_argument& e)
{
LOG_FAIL_FMT(
"{}: Could not parse x5c of key id {}: {}",
log_prefix,
jwk.kid,
e.what());
return false;
}
std::map<std::string, std::vector<uint8_t>> claims;
bool has_key_policy_sgx_claims = issuer_metadata.key_policy.has_value() &&
issuer_metadata.key_policy.value().sgx_claims.has_value() &&
!issuer_metadata.key_policy.value().sgx_claims.value().empty();
if (
issuer_metadata.key_filter == JwtIssuerKeyFilter::SGX ||
has_key_policy_sgx_claims)
{
oe_verify_attestation_certificate_with_evidence(
der.data(),
der.size(),
oe_verify_attestation_certificate_with_evidence_cb,
&claims);
}
if (
issuer_metadata.key_filter == JwtIssuerKeyFilter::SGX && claims.empty())
{
LOG_INFO_FMT(
"{}: Skipping JWT signing key with kid {} (not OE "
"attested)",
log_prefix,
jwk.kid);
continue;
}
if (has_key_policy_sgx_claims)
{
for (auto& [claim_name, expected_claim_val_hex] :
issuer_metadata.key_policy.value().sgx_claims.value())
{
if (claims.find(claim_name) == claims.end())
{
LOG_FAIL_FMT(
"{}: JWKS kid {} is missing the {} SGX claim",
log_prefix,
jwk.kid,
claim_name);
return false;
}
auto& actual_claim_val = claims[claim_name];
auto actual_claim_val_hex = ds::to_hex(actual_claim_val);
if (expected_claim_val_hex != actual_claim_val_hex)
{
LOG_FAIL_FMT(
"{}: JWKS kid {} has a mismatching {} SGX claim: {} != {}",
log_prefix,
jwk.kid,
claim_name,
expected_claim_val_hex,
actual_claim_val_hex);
return false;
}
}
}
else
{
try
{
crypto::check_is_cert(der);
}
catch (std::invalid_argument& exc)
{
LOG_FAIL_FMT(
"{}: JWKS kid {} has an invalid X.509 certificate: {}",
log_prefix,
jwk.kid,
exc.what());
return false;
}
}
LOG_INFO_FMT(
"{}: Storing JWT signing key with kid {}", log_prefix, jwk.kid);
new_keys.emplace(jwk.kid, der);
}
if (new_keys.empty())
{
LOG_FAIL_FMT("{}: no keys left after applying filter", log_prefix);
return false;
}
remove_jwt_public_signing_keys(tx, issuer);
for (auto& [kid, der] : new_keys)
{
keys->put(kid, der);
key_issuer->put(kid, issuer);
}
return true;
}