Index | Thread | Search

From:
Job Snijders <job@openbsd.org>
Subject:
rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output
To:
tech@openbsd.org
Date:
Sat, 6 Sep 2025 05:15:04 +0000

Download raw body.

Thread
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);