Skip to content

Commit

Permalink
WIP JWT raw keys
Browse files Browse the repository at this point in the history
  • Loading branch information
maxtropets committed Dec 2, 2024
1 parent 04a550f commit 4d5d073
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 212 deletions.
56 changes: 3 additions & 53 deletions doc/schemas/gov_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -291,27 +291,6 @@
},
"type": "object"
},
"KeyIdInfo": {
"properties": {
"cert": {
"$ref": "#/components/schemas/Pem"
},
"issuer": {
"$ref": "#/components/schemas/string"
}
},
"required": [
"issuer",
"cert"
],
"type": "object"
},
"KeyIdInfo_array": {
"items": {
"$ref": "#/components/schemas/KeyIdInfo"
},
"type": "array"
},
"MDType": {
"enum": [
"NONE",
Expand Down Expand Up @@ -808,10 +787,12 @@
},
"issuer": {
"$ref": "#/components/schemas/string"
},
"public_key": {
"$ref": "#/components/schemas/base64string"
}
},
"required": [
"cert",
"issuer"
],
"type": "object"
Expand Down Expand Up @@ -1222,12 +1203,6 @@
},
"type": "object"
},
"string_to_KeyIdInfo_array": {
"additionalProperties": {
"$ref": "#/components/schemas/KeyIdInfo_array"
},
"type": "object"
},
"string_to_OpenIDJWKMetadata_array": {
"additionalProperties": {
"$ref": "#/components/schemas/OpenIDJWKMetadata_array"
Expand Down Expand Up @@ -1473,31 +1448,6 @@
}
]
},
"/gov/jwt_keys/all": {
"get": {
"deprecated": true,
"description": "This endpoint is deprecated from 5.0.0. It is replaced by POST /gov/service/jwk",
"operationId": "GetGovJwtKeysAll",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/string_to_KeyIdInfo_array"
}
}
},
"description": "Default response description"
},
"default": {
"$ref": "#/components/responses/default"
}
},
"x-ccf-forwarding": {
"$ref": "#/components/x-ccf-forwarding/always"
}
}
},
"/gov/kv/constitution": {
"get": {
"deprecated": true,
Expand Down
18 changes: 16 additions & 2 deletions include/ccf/crypto/jwk.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,27 @@ namespace ccf::crypto
JsonWebKeyType kty;
std::optional<std::string> kid = std::nullopt;
std::optional<std::vector<std::string>> x5c = std::nullopt;
std::optional<std::string> issuer = std::nullopt;

bool operator==(const JsonWebKey&) const = default;
};
DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(JsonWebKey);
DECLARE_JSON_REQUIRED_FIELDS(JsonWebKey, kty);
DECLARE_JSON_OPTIONAL_FIELDS(JsonWebKey, kid, x5c, issuer);
DECLARE_JSON_OPTIONAL_FIELDS(JsonWebKey, kid, x5c);

struct JsonWebKeyExtended
{
JsonWebKeyType kty;
std::optional<std::string> kid = std::nullopt;
std::optional<std::vector<std::string>> x5c = std::nullopt;
std::optional<std::string> n = std::nullopt;
std::optional<std::string> e = std::nullopt;
std::optional<std::string> issuer = std::nullopt;

bool operator==(const JsonWebKeyExtended&) const = default;
};
DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(JsonWebKeyExtended);
DECLARE_JSON_REQUIRED_FIELDS(JsonWebKeyExtended, kty);
DECLARE_JSON_OPTIONAL_FIELDS(JsonWebKeyExtended, kid, x5c, n, e, issuer);

enum class JsonWebKeyECCurve
{
Expand Down
10 changes: 6 additions & 4 deletions include/ccf/service/tables/jwt.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ namespace ccf
using JwtIssuer = std::string;
using JwtKeyId = std::string;
using Cert = std::vector<uint8_t>;
using PublicKey = std::vector<uint8_t>;

struct OpenIDJWKMetadata
{
Cert cert;
std::optional<Cert> cert;
std::optional<PublicKey> public_key;
JwtIssuer issuer;
std::optional<JwtIssuer> constraint;
};
DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(OpenIDJWKMetadata);
DECLARE_JSON_REQUIRED_FIELDS(OpenIDJWKMetadata, cert, issuer);
DECLARE_JSON_OPTIONAL_FIELDS(OpenIDJWKMetadata, constraint);
DECLARE_JSON_REQUIRED_FIELDS(OpenIDJWKMetadata, issuer);
DECLARE_JSON_OPTIONAL_FIELDS(OpenIDJWKMetadata, cert, public_key, constraint);

using JwtIssuers = ServiceMap<JwtIssuer, JwtIssuerMetadata>;
using JwtPublicSigningKeys =
Expand Down Expand Up @@ -75,7 +77,7 @@ namespace ccf

struct JsonWebKeySet
{
std::vector<ccf::crypto::JsonWebKey> keys;
std::vector<ccf::crypto::JsonWebKeyExtended> keys;

bool operator!=(const JsonWebKeySet& rhs) const
{
Expand Down
25 changes: 16 additions & 9 deletions samples/constitutions/default/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,22 @@ function checkJwks(value, field) {
for (const [i, jwk] of value.keys.entries()) {
checkType(jwk.kid, "string", `${field}.keys[${i}].kid`);
checkType(jwk.kty, "string", `${field}.keys[${i}].kty`);
checkType(jwk.x5c, "array", `${field}.keys[${i}].x5c`);
checkLength(jwk.x5c, 1, null, `${field}.keys[${i}].x5c`);
for (const [j, b64der] of jwk.x5c.entries()) {
checkType(b64der, "string", `${field}.keys[${i}].x5c[${j}]`);
const pem =
"-----BEGIN CERTIFICATE-----\n" +
b64der +
"\n-----END CERTIFICATE-----";
checkX509CertBundle(pem, `${field}.keys[${i}].x5c[${j}]`);
if (jwk.x5c) {
checkType(jwk.x5c, "array", `${field}.keys[${i}].x5c`);
checkLength(jwk.x5c, 1, null, `${field}.keys[${i}].x5c`);
for (const [j, b64der] of jwk.x5c.entries()) {
checkType(b64der, "string", `${field}.keys[${i}].x5c[${j}]`);
const pem =
"-----BEGIN CERTIFICATE-----\n" +
b64der +
"\n-----END CERTIFICATE-----";
checkX509CertBundle(pem, `${field}.keys[${i}].x5c[${j}]`);
}
} else if (jwk.n && jwk.e) {
checkType(jwk.n, "string", `${field}.keys[${i}].n`);
checkType(jwk.e, "string", `${field}.keys[${i}].e`);
} else {
throw new Error("JWK must contain either x5c or n and e");
}
}
}
Expand Down
32 changes: 29 additions & 3 deletions src/endpoints/authentication/jwt_auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "ccf/endpoints/authentication/jwt_auth.h"

#include "ccf/crypto/rsa_key_pair.h"
#include "ccf/ds/nonstd.h"
#include "ccf/pal/locking.h"
#include "ccf/rpc_context.h"
Expand Down Expand Up @@ -146,6 +147,7 @@ namespace ccf
{
token_keys = std::vector<OpenIDJWKMetadata>{OpenIDJWKMetadata{
.cert = *fallback_key,
.public_key = std::nullopt,
.issuer = *fallback_issuers->get(key_id),
.constraint = std::nullopt}};
}
Expand All @@ -160,10 +162,34 @@ namespace ccf

for (const auto& metadata : *token_keys)
{
auto verifier = verifiers->get_verifier(metadata.cert);
if (!::http::JwtVerifier::validate_token_signature(token, verifier))
if (metadata.public_key.has_value())
{
error_reason = "Signature verification failed";
auto pubkey =
ccf::crypto::make_rsa_public_key(metadata.public_key.value());
// To Do cache
if (!pubkey->verify(
(uint8_t*)token.signed_content.data(),
token.signed_content.size(),
token.signature.data(),
token.signature.size(),
ccf::crypto::MDType::SHA256))
{
error_reason = "Signature verification failed (raw public key)";
continue;
}
}
else if (metadata.cert.has_value())
{
auto verifier = verifiers->get_verifier(metadata.cert.value());
if (!::http::JwtVerifier::validate_token_signature(token, verifier))
{
error_reason = "Signature verification failed (certificate)";
continue;
}
}
else
{
error_reason = "Missing public key or certificate";
continue;
}

Expand Down
27 changes: 23 additions & 4 deletions src/node/gov/handlers/service_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,10 +578,29 @@ namespace ccf::gov::endpoints
{
auto info = nlohmann::json::object();

// cert is stored as DER - convert to PEM for API
const auto cert_pem =
ccf::crypto::cert_der_to_pem(metadata.cert);
info["certificate"] = cert_pem.str();
std::string key{}, value{};
if (metadata.public_key.has_value())
{
key = "publicKey";
value = ccf::crypto::make_rsa_public_key(
metadata.public_key.value())
->public_key_pem()
.str();
}
else if (metadata.cert.has_value())
{
key = "certificate";
value =
ccf::crypto::cert_der_to_pem(metadata.cert.value()).str();
}
else
{
// This must not happen, but we intentionally ignore it here
// as this is just a key reporting endpoint, this situation
// must be prevented at the time of storing the new keys.
}

info[key] = value;

info["issuer"] = metadata.issuer;
info["constraint"] = metadata.constraint;
Expand Down
Loading

0 comments on commit 4d5d073

Please sign in to comment.