Download raw body.
rpki-client: add support for router keys in CCR
This changeset implements support for router keys in the CCR format.
The objective here is to have full coverage for RFC8210/RFC8210bis.
OK?
Index: ccr.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/ccr.c,v
diff -u -p -r1.9 ccr.c
--- ccr.c 11 Sep 2025 09:25:05 -0000 1.9
+++ ccr.c 14 Sep 2025 10:34:56 -0000
@@ -46,6 +46,9 @@ ASN1_ITEM_EXP ASPAPayloadSets_it;
ASN1_ITEM_EXP ASPAPayloadSet_it;
ASN1_ITEM_EXP SubjectKeyIdentifiers_it;
ASN1_ITEM_EXP SubjectKeyIdentifier_it;
+ASN1_ITEM_EXP RouterKeySets_it;
+ASN1_ITEM_EXP RouterKeySet_it;
+ASN1_ITEM_EXP RouterKey_it;
/*
* Can't use CMS_ContentInfo since it is not backed by a public struct
@@ -67,6 +70,7 @@ ASN1_SEQUENCE(CanonicalCacheRepresentati
ASN1_EXP_OPT(CanonicalCacheRepresentation, vrps, ROAPayloadState, 2),
ASN1_EXP_OPT(CanonicalCacheRepresentation, vaps, ASPAPayloadState, 3),
ASN1_EXP_OPT(CanonicalCacheRepresentation, tas, TrustAnchorState, 4),
+ ASN1_EXP_OPT(CanonicalCacheRepresentation, rks, RouterKeyState, 5),
} ASN1_SEQUENCE_END(CanonicalCacheRepresentation);
IMPLEMENT_ASN1_FUNCTIONS(CanonicalCacheRepresentation);
@@ -157,6 +161,34 @@ ASN1_ITEM_TEMPLATE_END(SubjectKeyIdentif
IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(SubjectKeyIdentifiers,
SubjectKeyIdentifiers, SubjectKeyIdentifiers);
+ASN1_SEQUENCE(RouterKeyState) = {
+ ASN1_SEQUENCE_OF(RouterKeyState, rksets, RouterKeySet),
+ ASN1_SIMPLE(RouterKeyState, hash, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(RouterKeyState);
+
+IMPLEMENT_ASN1_FUNCTIONS(RouterKeyState);
+
+ASN1_ITEM_TEMPLATE(RouterKeySets) =
+ ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, rks, RouterKeySet)
+ASN1_ITEM_TEMPLATE_END(RouterKeySets);
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(RouterKeySets, RouterKeySets,
+ RouterKeySets);
+
+ASN1_SEQUENCE(RouterKeySet) = {
+ ASN1_SIMPLE(RouterKeySet, asID, ASN1_INTEGER),
+ ASN1_SEQUENCE_OF(RouterKeySet, routerKeys, RouterKey),
+} ASN1_SEQUENCE_END(RouterKeySet);
+
+IMPLEMENT_ASN1_FUNCTIONS(RouterKeySet);
+
+ASN1_SEQUENCE(RouterKey) = {
+ ASN1_SIMPLE(RouterKey, ski, SubjectKeyIdentifier),
+ ASN1_SIMPLE(RouterKey, spki, X509_PUBKEY),
+} ASN1_SEQUENCE_END(RouterKey);
+
+IMPLEMENT_ASN1_FUNCTIONS(RouterKey);
+
static void
hash_asn1_item(ASN1_OCTET_STRING *astr, const ASN1_ITEM *it, void *val)
{
@@ -471,6 +503,71 @@ generate_trustanchorstate(struct validat
return tas;
}
+static RouterKeyState *
+generate_routerkeystate(struct validation_data *vd)
+{
+ RouterKeyState *rks;
+ RouterKeySet *rkset;
+ RouterKey *rk;
+ struct brk *brk, *prev;
+ unsigned char *pk_der = NULL;
+ size_t pk_len;
+ const unsigned char *der;
+
+ if ((rks = RouterKeyState_new()) == NULL)
+ errx(1, "RouterKeyState_new");
+
+ prev = NULL;
+ RB_FOREACH(brk, brk_tree, &vd->brks) {
+ static char hbuf[SHA_DIGEST_LENGTH] = { 0 };
+
+ if (prev == NULL || prev->asid != brk->asid) {
+ if ((rkset = RouterKeySet_new()) == NULL)
+ errx(1, "RouterKeySet_new");
+
+ if (!ASN1_INTEGER_set_uint64(rkset->asID, brk->asid))
+ errx(1, "ASN1_INTEGER_set_uint64");
+
+ if (sk_RouterKeySet_push(rks->rksets, rkset) <= 0)
+ errx(1, "sk_RouterKeySet_push");
+ }
+
+ if ((rk = RouterKey_new()) == NULL)
+ errx(1, "RouterKey_new");
+
+ if (hex_decode(brk->ski, hbuf, sizeof(hbuf)) == -1)
+ errx(1, "hex_decode brk pubkey corrupted");
+
+ if (!ASN1_OCTET_STRING_set(rk->ski, hbuf, sizeof(hbuf)))
+ errx(1, "ASN1_OCTET_STRING_set");
+
+ if (base64_decode(brk->pubkey, strlen(brk->pubkey), &pk_der,
+ &pk_len) == -1)
+ errx(1, "base64_decode");
+
+ X509_PUBKEY_free(rk->spki);
+ der = pk_der;
+ if ((rk->spki = d2i_X509_PUBKEY(NULL, &der, pk_len)) == NULL)
+ errx(1, "d2i_X509_PUBKEY");
+ if (der != pk_der + pk_len)
+ errx(1, "brk pubkey corrupted");
+ free(pk_der);
+ pk_der = NULL;
+
+ if ((sk_RouterKey_push(rkset->routerKeys, rk)) <= 0)
+ errx(1, "sk_RouterKey_push");
+
+ prev = brk;
+ }
+
+ hash_asn1_item(rks->hash, ASN1_ITEM_rptr(RouterKeySets), rks->rksets);
+
+ if (!base64_encode_asn1str(rks->hash, &vd->ccr.brks_hash))
+ errx(1, "base64_encode_asn1str");
+
+ return rks;
+}
+
static CanonicalCacheRepresentation *
generate_ccr(struct validation_data *vd)
{
@@ -499,6 +596,9 @@ generate_ccr(struct validation_data *vd)
if ((ccr->tas = generate_trustanchorstate(vd)) == NULL)
errx(1, "generate_trustanchorstate");
+ if ((ccr->rks = generate_routerkeystate(vd)) == NULL)
+ errx(1, "generate_routerkeystate");
+
return ccr;
}
@@ -743,6 +843,28 @@ ccr_tas_free(struct ccr_tas_tree *tas)
}
}
+static void
+brk_free(struct brk *brk)
+{
+ if (brk == NULL)
+ return;
+
+ free(brk->ski);
+ free(brk->pubkey);
+ free(brk);
+}
+
+static void
+ccr_brks_free(struct brk_tree *brks)
+{
+ struct brk *brk, *tmp_brk;
+
+ RB_FOREACH_SAFE(brk, brk_tree, brks, tmp_brk) {
+ RB_REMOVE(brk_tree, brks, brk);
+ brk_free(brk);
+ }
+}
+
void
ccr_free(struct ccr *ccr)
{
@@ -753,6 +875,7 @@ ccr_free(struct ccr *ccr)
ccr_vrps_free(&ccr->vrps);
ccr_vaps_free(&ccr->vaps);
ccr_tas_free(&ccr->tas);
+ ccr_brks_free(&ccr->brks);
free(ccr->mfts_hash);
free(ccr->vrps_hash);
@@ -1216,6 +1339,127 @@ parse_tastate(const char *fn, struct ccr
return rc;
}
+static int
+parse_routerkeys(const char *fn, struct ccr *ccr, uint32_t asid,
+ STACK_OF(RouterKey) *routerkeys)
+{
+ RouterKey *rk;
+ struct brk *brk, *prev;
+ int i, rk_num, rc = 0;
+
+ if ((rk_num = sk_RouterKey_num(routerkeys)) <= 0) {
+ warnx("%s: missing RouterKey", fn);
+ goto out;
+ }
+
+ prev = NULL;
+ for (i = 0; i < rk_num; i++) {
+ unsigned char *der = NULL;
+ size_t der_len;
+
+ if ((brk = calloc(1, sizeof(*brk))) == NULL)
+ err(1, NULL);
+
+ brk->asid = asid;
+
+ rk = sk_RouterKey_value(routerkeys, i);
+
+ if (rk->ski->length != SHA_DIGEST_LENGTH) {
+ warnx("%s: AS%d RouterKey SKI corrupted", fn, asid);
+ goto out;
+ }
+
+ brk->ski = hex_encode(rk->ski->data, rk->ski->length);
+ if (brk->ski == NULL)
+ goto out;
+
+ if ((der_len = i2d_X509_PUBKEY(rk->spki, &der)) <= 0) {
+ warnx("%s: AS%d RouterKey SPKI corrupted", fn, asid);
+ goto out;
+ }
+
+ if (base64_encode(der, der_len, &brk->pubkey) == -1)
+ errx(1, "base64_encode");
+
+ free(der);
+
+ if (prev != NULL) {
+ if (strcmp(brk->ski, prev->ski) <= 0) {
+ warnx("%s: misordered RouterKey", fn);
+ goto out;
+ }
+ }
+
+ if ((RB_INSERT(brk_tree, &ccr->brks, brk)) != NULL) {
+ warnx("%s; duplicate RouterKey", fn);
+ goto out;
+ }
+
+ prev = brk;
+ brk = NULL;
+ }
+
+ rc = 1;
+ out:
+ return rc;
+}
+
+static int
+parse_rksets(const char *fn, struct ccr *ccr, STACK_OF(RouterKeySet) *rksets)
+{
+ RouterKeySet *rkset;
+ uint32_t asid, prev = 0;
+ int i, rc = 0, rksets_num;
+
+ rksets_num = sk_RouterKeySet_num(rksets);
+
+ RB_INIT(&ccr->brks);
+
+ for (i = 0; i < rksets_num; i++) {
+ rkset = sk_RouterKeySet_value(rksets, i);
+
+ if (!as_id_parse(rkset->asID, &asid)) {
+ warnx("%s: malformed asID in RouterKeySet", fn);
+ goto out;
+ }
+
+ if (i > 0) {
+ if (asid <= prev) {
+ warnx("%s: AS %d misordered routerkeyset", fn,
+ asid);
+ goto out;
+ }
+ }
+
+ if (!parse_routerkeys(fn, ccr, asid, rkset->routerKeys))
+ goto out;
+
+ prev = asid;
+ }
+
+ rc = 1;
+ out:
+ return rc;
+}
+
+static int
+parse_rkstate(const char *fn, struct ccr *ccr, const RouterKeyState *state)
+{
+ int rc = 0;
+
+ ccr->brks_hash = validate_asn1_hash(fn, "RouterKeyState", state->hash,
+ ASN1_ITEM_rptr(RouterKeySets), state->rksets);
+ if (ccr->brks_hash == NULL)
+ goto out;
+
+ if (!parse_rksets(fn, ccr, state->rksets))
+ goto out;
+
+ rc = 1;
+ out:
+ return rc;
+}
+
struct ccr *
ccr_parse(const char *fn, const unsigned char *der, size_t len)
{
@@ -1278,7 +1522,8 @@ ccr_parse(const char *fn, const unsigned
goto out;
if (ccr_asn1->mfts == NULL && ccr_asn1->vrps == NULL &&
- ccr_asn1->vaps == NULL && ccr_asn1->tas == NULL) {
+ ccr_asn1->vaps == NULL && ccr_asn1->tas == NULL &&
+ ccr_asn1->rks == NULL) {
warnx("%s: must contain at least one state component", fn);
goto out;
}
@@ -1300,6 +1545,11 @@ ccr_parse(const char *fn, const unsigned
if (ccr_asn1->tas != NULL) {
if (!parse_tastate(fn, ccr, ccr_asn1->tas))
+ goto out;
+ }
+
+ if (ccr_asn1->rks != NULL) {
+ if (!parse_rkstate(fn, ccr, ccr_asn1->rks))
goto out;
}
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
diff -u -p -r1.263 extern.h
--- extern.h 11 Sep 2025 08:21:00 -0000 1.263
+++ extern.h 14 Sep 2025 10:34:56 -0000
@@ -490,10 +490,12 @@ struct ccr {
struct ccr_vrp_tree vrps;
struct vap_tree vaps; /* only used in filemode */
struct ccr_tas_tree tas;
+ struct brk_tree brks; /* only used in filemode */
char *mfts_hash;
char *vrps_hash;
char *vaps_hash;
char *tas_hash;
+ char *brks_hash;
time_t producedat;
time_t most_recent_update;
unsigned char *der;
Index: print.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/print.c,v
diff -u -p -r1.66 print.c
--- print.c 9 Sep 2025 08:23:24 -0000 1.66
+++ print.c 14 Sep 2025 10:34:56 -0000
@@ -1073,6 +1073,42 @@ print_ccr_tastate(struct ccr *ccr)
}
}
+static void
+print_ccr_rkstate(struct ccr *ccr)
+{
+ char *hash;
+ struct brk *brk;
+
+ if (base64_encode(ccr->brks_hash, SHA256_DIGEST_LENGTH, &hash) == -1)
+ errx(1, "base64_encode");
+
+ if (outformats & FORMAT_JSON) {
+ json_do_object("routerkey_state", 0);
+ json_do_string("hash", hash);
+ json_do_array("routerkeys");
+ RB_FOREACH(brk, brk_tree, &ccr->brks) {
+ json_do_object("brk", 0);
+ json_do_int("asn", brk->asid);
+ json_do_string("ski", brk->ski);
+ json_do_string("pubkey", brk->pubkey);
+ json_do_end(); // brk
+ }
+ json_do_end(); // routerkeys
+ json_do_end(); // routerkey_state
+ } else {
+ printf("Router key state hash: %s\n", hash);
+ printf("Router keys:\n");
+ RB_FOREACH(brk, brk_tree, &ccr->brks) {
+ printf("%26s", "");
+ printf("asid:%d ", brk->asid);
+ printf("ski:%s ", brk->ski);
+ printf("pubkey:%s\n", brk->pubkey);
+ }
+ }
+
+ free(hash);
+}
+
void
ccr_print(struct ccr *ccr)
{
@@ -1095,4 +1131,7 @@ ccr_print(struct ccr *ccr)
if (ccr->tas_hash != NULL)
print_ccr_tastate(ccr);
+
+ if (ccr->brks_hash != NULL)
+ print_ccr_rkstate(ccr);
}
Index: rpki-asn1.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/rpki-asn1.h,v
diff -u -p -r1.5 rpki-asn1.h
--- rpki-asn1.h 11 Sep 2025 08:21:00 -0000 1.5
+++ rpki-asn1.h 14 Sep 2025 10:34:56 -0000
@@ -23,6 +23,7 @@
#include <openssl/asn1.h>
#include <openssl/asn1t.h>
+#include <openssl/x509.h>
/*
* Autonomous System Provider Authorization (ASPA)
@@ -55,6 +56,9 @@ extern ASN1_ITEM_EXP ASPAPayloadSets_it;
extern ASN1_ITEM_EXP ASPAPayloadSet_it;
extern ASN1_ITEM_EXP SubjectKeyIdentifiers_it;
extern ASN1_ITEM_EXP SubjectKeyIdentifier_it;
+extern ASN1_ITEM_EXP RouterKeySets_it;
+extern ASN1_ITEM_EXP RouterKeySet_it;
+extern ASN1_ITEM_EXP RouterKey_it;
typedef struct {
ASN1_OCTET_STRING *hash;
@@ -164,6 +168,47 @@ typedef struct {
DECLARE_ASN1_FUNCTIONS(TrustAnchorState);
typedef struct {
+ STACK_OF(RouterKeySet) *rksets;
+ ASN1_OCTET_STRING *hash;
+} RouterKeyState;
+
+DECLARE_ASN1_FUNCTIONS(RouterKeyState);
+
+typedef struct {
+ ASN1_INTEGER *asID;
+ STACK_OF(RouterKey) *routerKeys;
+} RouterKeySet;
+
+DECLARE_STACK_OF(RouterKeySet);
+
+#ifndef DEFINE_STACK_OF
+#define sk_RouterKeySet_num(st) SKM_sk_num(RouterKeySet, (st))
+#define sk_RouterKeySet_push(st, i) SKM_sk_push(RouterKeySet, (st), (i))
+#define sk_RouterKeySet_value(st, i) SKM_sk_value(RouterKeySet, (st), (i))
+#endif
+
+DECLARE_ASN1_FUNCTIONS(RouterKeySet);
+
+typedef STACK_OF(RouterKeySet) RouterKeySets;
+
+DECLARE_ASN1_FUNCTIONS(RouterKeySets);
+
+typedef struct {
+ SubjectKeyIdentifier *ski;
+ X509_PUBKEY *spki;
+} RouterKey;
+
+DECLARE_STACK_OF(RouterKey);
+
+#ifndef DEFINE_STACK_OF
+#define sk_RouterKey_num(st) SKM_sk_num(RouterKey, (st))
+#define sk_RouterKey_push(st, i) SKM_sk_push(RouterKey, (st), (i))
+#define sk_RouterKey_value(st, i) SKM_sk_value(RouterKey, (st), (i))
+#endif
+
+DECLARE_ASN1_FUNCTIONS(RouterKey);
+
+typedef struct {
ASN1_INTEGER *version;
ASN1_OBJECT *hashAlg;
ASN1_GENERALIZEDTIME *producedAt;
@@ -171,6 +216,7 @@ typedef struct {
ROAPayloadState *vrps;
ASPAPayloadState *vaps;
TrustAnchorState *tas;
+ RouterKeyState *rks;
} CanonicalCacheRepresentation;
DECLARE_ASN1_FUNCTIONS(CanonicalCacheRepresentation);
rpki-client: add support for router keys in CCR