From: Job Snijders Subject: rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output To: tech@openbsd.org Date: Sat, 6 Sep 2025 05:15:04 +0000 This diff makes it so that the SubjectKeyIdentifiers of the valid & used Trust Anchor certificates are added to the CCR output. $ cat /var/db/rpki-client/rpki.ccr | der2ascii | tail -n 13 | head -n 9 [4] { SEQUENCE { OCTET_STRING { `0b9cca90dd0d7a8a37666b19217fe0d84037b7a2` } OCTET_STRING { `13d4f24f9a9fcd98db36f930631808c88f3974bc` } OCTET_STRING { `e8552b1fd6d1a4f7e404c6d8e5680d1ebc163fc3` } OCTET_STRING { `eb680f38f5d6c71bb4b106b8bd06585012da31b6` } OCTET_STRING { `fc8a9cb3ed184e17d30eea1e0fa7615ce4b1af47` } } } OK? Index: ccr.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/ccr.c,v diff -u -p -r1.2 ccr.c --- ccr.c 23 Aug 2025 11:16:50 -0000 1.2 +++ ccr.c 6 Sep 2025 05:09:26 -0000 @@ -41,15 +41,10 @@ * BEGIN * * IMPORTS - * CONTENT-TYPE, Digest, DigestAlgorithmIdentifier + * CONTENT-TYPE, Digest, DigestAlgorithmIdentifier, SubjectKeyIdentifier * FROM CryptographicMessageSyntax-2010 -- in [RFC6268] * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) - * pkcs-9(9) smime(16) modules(0) id-mod-cms-2009(58) }; - * - * KeyIdentifier - * FROM PKIX1Implicit88 -- in [RFC5280] - * { iso(1) identified-organization(3) dod(6) internet(1) security(5) - * mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19) } + * pkcs-9(9) smime(16) modules(0) id-mod-cms-2009(58) } * * -- in [draft-spaghetti-sidrops-rpki-erik-protocol-01] * -- https://sobornost.net/~job/draft-spaghetti-sidrops-rpki-erik-protocol.html @@ -62,6 +57,7 @@ * FROM RPKI-ROA-2023 -- in [RFC9582] * { so(1) member-body(2) us(840) rsadsi(113549) pkcs(1) * pkcs9(9) smime(16) mod(0) id-mod-rpkiROA-2023(75) } + * ; * * ct-rpkiCanonicalCacheRepresentation CONTENT-TYPE ::= * { TYPE RpkiCanonicalCacheRepresentation @@ -78,11 +74,13 @@ * mfts [1] ManifestState OPTIONAL, * vrps [2] ROAPayloadState OPTIONAL, * vaps [3] ASPAPayloadState OPTIONAL, + * taskis [4] SEQUENCE (SIZE(1..MAX)) OF SubjectKeyIdentifier OPTIONAL, * ... } - * -- at least one of mfts, vrps or vaps MUST be present + * -- at least one of mfts, vrps, vaps, or taskis MUST be present * ( WITH COMPONENTS { ..., mfts PRESENT } | * WITH COMPONENTS { ..., vrps PRESENT } | - * WITH COMPONENTS { ..., vaps PRESENT } ) + * WITH COMPONENTS { ..., vaps PRESENT } | + * WITH COMPONENTS { ..., taskis PRESENT } ) * * ManifestState ::= SEQUENCE { * mftrefs SEQUENCE OF ManifestRef, @@ -136,6 +134,8 @@ ASN1_SEQUENCE(CanonicalCacheRepresentati ASN1_EXP_OPT(CanonicalCacheRepresentation, mfts, ManifestState, 1), ASN1_EXP_OPT(CanonicalCacheRepresentation, vrps, ROAPayloadState, 2), ASN1_EXP_OPT(CanonicalCacheRepresentation, vaps, ASPAPayloadState, 3), + ASN1_EXP_SEQUENCE_OF_OPT(CanonicalCacheRepresentation, taskis, + ASN1_OCTET_STRING, 4), } ASN1_SEQUENCE_END(CanonicalCacheRepresentation); IMPLEMENT_ASN1_FUNCTIONS(CanonicalCacheRepresentation); @@ -456,6 +456,31 @@ generate_aspapayloadstate(struct validat return vaps; } +static STACK_OF(ASN1_OCTET_STRING) * +generate_taskis(struct validation_data *vd) +{ + STACK_OF(ASN1_OCTET_STRING) *taskis = NULL; + ASN1_OCTET_STRING *astr = NULL; + struct ccr_taski *taski; + + if ((taskis = sk_ASN1_OCTET_STRING_new_null()) == NULL) + errx(1, "sk_ASN1_OCTET_STRING_new_null"); + + RB_FOREACH(taski, ccr_taski_tree, &vd->ccr.taskis) { + if ((astr = ASN1_OCTET_STRING_new()) == NULL) + errx(1, "ASN1_OCTET_STRING_new"); + + if (!ASN1_OCTET_STRING_set(astr, taski->keyid, + sizeof(taski->keyid))) + errx(1, "ASN1_OCTET_STRING_set"); + + if ((sk_ASN1_OCTET_STRING_push(taskis, astr)) <= 0) + errx(1, "sk_ASN1_OCTET_STRING_push"); + } + + return taskis; +} + static CanonicalCacheRepresentation * generate_ccr(struct validation_data *vd) { @@ -481,6 +506,9 @@ generate_ccr(struct validation_data *vd) if ((ccr->vaps = generate_aspapayloadstate(vd)) == NULL) errx(1, "generate_aspapayloadstate"); + if ((ccr->taskis = generate_taskis(vd)) == NULL) + errx(1, "generate_taskis"); + return ccr; } @@ -520,6 +548,30 @@ serialize_ccr_content(struct validation_ ContentInfo_free(ci); } +static inline int +ccr_taski_cmp(const struct ccr_taski *a, const struct ccr_taski *b) +{ + return memcmp(a->keyid, b->keyid, SHA_DIGEST_LENGTH); +} + +RB_GENERATE(ccr_taski_tree, ccr_taski, entry, ccr_taski_cmp); + +void +ccr_insert_taski(struct ccr_taski_tree *tree, const struct cert *cert) +{ + struct ccr_taski *taski; + + assert(cert->aki == NULL && cert->aia == NULL); + + if ((taski = calloc(1, sizeof(*taski))) == NULL) + err(1, NULL); + + if ((hex_decode(cert->ski, taski->keyid, sizeof(taski->keyid))) != 0) + err(1, NULL); + + if (RB_INSERT(ccr_taski_tree, tree, taski) != NULL) + errx(1, "CCR TASKI tree corrupted"); +} static inline int ccr_mft_cmp(const struct ccr_mft *a, const struct ccr_mft *b) Index: extern.h =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v diff -u -p -r1.260 extern.h --- extern.h 24 Aug 2025 12:17:12 -0000 1.260 +++ extern.h 6 Sep 2025 05:09:26 -0000 @@ -476,9 +476,18 @@ RB_PROTOTYPE(ccr_mft_tree, ccr_mft, entr RB_HEAD(ccr_vrp_tree, vrp); RB_PROTOTYPE(ccr_vrp_tree, vrp, entry, ccr_vrp_cmp); +struct ccr_taski { + RB_ENTRY(ccr_taski) entry; + unsigned char keyid[SHA_DIGEST_LENGTH]; +}; + +RB_HEAD(ccr_taski_tree, ccr_taski); +RB_PROTOTYPE(ccr_taski_tree, ccr_taski, entry, ccr_taski_cmp); + struct ccr { struct ccr_mft_tree mfts; struct ccr_vrp_tree vrps; + struct ccr_taski_tree taskis; char *mfts_hash; char *vrps_hash; char *vaps_hash; @@ -1018,6 +1027,7 @@ int output_ccr_der(FILE *, struct vali */ void ccr_insert_mft(struct ccr_mft_tree *, const struct mft *); void ccr_insert_roa(struct ccr_vrp_tree *, const struct roa *); +void ccr_insert_taski(struct ccr_taski_tree *, const struct cert *); void serialize_ccr_content(struct validation_data *); void logx(const char *fmt, ...) Index: main.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/main.c,v diff -u -p -r1.296 main.c --- main.c 5 Sep 2025 17:38:03 -0000 1.296 +++ main.c 6 Sep 2025 05:09:26 -0000 @@ -634,6 +634,8 @@ entity_process(struct ibuf *b, struct va cert = cert_read(b); switch (cert->purpose) { case CERT_PURPOSE_TA: + ccr_insert_taski(&vd->ccr.taskis, cert); + /* FALLTHROUGH */ case CERT_PURPOSE_CA: queue_add_from_cert(cert, &vd->ncas); break; @@ -1025,6 +1027,7 @@ main(int argc, char *argv[]) RB_INIT(&vd.ncas); RB_INIT(&vd.ccr.mfts); RB_INIT(&vd.ccr.vrps); + RB_INIT(&vd.ccr.taskis); /* If started as root, priv-drop to _rpki-client */ if (getuid() == 0) { Index: rpki-asn1.h =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/rpki-asn1.h,v diff -u -p -r1.2 rpki-asn1.h --- rpki-asn1.h 23 Aug 2025 09:13:14 -0000 1.2 +++ rpki-asn1.h 6 Sep 2025 05:09:26 -0000 @@ -133,6 +133,16 @@ typedef struct { DECLARE_ASN1_FUNCTIONS(ASPAPayloadState); +DECLARE_STACK_OF(ASN1_OCTET_STRING); + +#ifndef DEFINE_STACK_OF +#define sk_ASN1_OCTET_STRING_new_null() SKM_sk_new_null(ASN1_OCTET_STRING) +#define sk_ASN1_OCTET_STRING_push(st, i) \ + SKM_sk_push(ASN1_OCTET_STRING, (st), (i)) +#endif + +DECLARE_ASN1_FUNCTIONS(ASN1_OCTET_STRING); + typedef struct { ASN1_INTEGER *version; ASN1_OBJECT *hashAlg; @@ -140,6 +150,7 @@ typedef struct { ManifestState *mfts; ROAPayloadState *vrps; ASPAPayloadState *vaps; + STACK_OF(ASN1_OCTET_STRING) *taskis; } CanonicalCacheRepresentation; DECLARE_ASN1_FUNCTIONS(CanonicalCacheRepresentation);