Download raw body.
rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output
rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output
rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output
Here is a variant that is more consistent with the other *State fields:
$ cat /var/db/rpki-client/rpki.ccr | der2ascii | tail -n 16 | head -n 12
[4] {
SEQUENCE {
SEQUENCE {
OCTET_STRING { `0b9cca90dd0d7a8a37666b19217fe0d84037b7a2` }
OCTET_STRING { `13d4f24f9a9fcd98db36f930631808c88f3974bc` }
OCTET_STRING { `e8552b1fd6d1a4f7e404c6d8e5680d1ebc163fc3` }
OCTET_STRING { `eb680f38f5d6c71bb4b106b8bd06585012da31b6` }
OCTET_STRING { `fc8a9cb3ed184e17d30eea1e0fa7615ce4b1af47` }
}
OCTET_STRING { `b9ba66b2bcd54e4812249f60ed2de9357670cc48ff848f1bc35f5986703de71f` }
}
}
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 07:29:11 -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,
+ * tas [4] TrustAnchorState OPTIONAL,
* ... }
- * -- at least one of mfts, vrps or vaps MUST be present
+ * -- at least one of mfts, vrps, vaps, or tas MUST be present
* ( WITH COMPONENTS { ..., mfts PRESENT } |
* WITH COMPONENTS { ..., vrps PRESENT } |
- * WITH COMPONENTS { ..., vaps PRESENT } )
+ * WITH COMPONENTS { ..., vaps PRESENT } |
+ * WITH COMPONENTS { ..., tas PRESENT } )
*
* ManifestState ::= SEQUENCE {
* mftrefs SEQUENCE OF ManifestRef,
@@ -105,6 +103,10 @@
* asID ASID
* providers SEQUENCE (SIZE(1..MAX)) OF ASID }
*
+ * TrustAnchorState ::= SEQUENCE {
+ * taskis SEQUENCE (SIZE(1..MAX)) OF SubjectKeyIdentifier,
+ * hash Digest }
+ *
* END
*/
@@ -116,6 +118,8 @@ ASN1_ITEM_EXP ROAPayloadSets_it;
ASN1_ITEM_EXP ROAPayloadSet_it;
ASN1_ITEM_EXP ASPAPayloadSets_it;
ASN1_ITEM_EXP ASPAPayloadSet_it;
+ASN1_ITEM_EXP TASubjectKeyIdentifiers_it;
+ASN1_ITEM_EXP SubjectKeyIdentifier_it;
/*
* Can't use CMS_ContentInfo since it is not backed by a public struct
@@ -136,6 +140,7 @@ 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_OPT(CanonicalCacheRepresentation, tas, TrustAnchorState, 4),
} ASN1_SEQUENCE_END(CanonicalCacheRepresentation);
IMPLEMENT_ASN1_FUNCTIONS(CanonicalCacheRepresentation);
@@ -208,6 +213,24 @@ ASN1_SEQUENCE(ASPAPayloadSet) = {
IMPLEMENT_ASN1_FUNCTIONS(ASPAPayloadSet);
+IMPLEMENT_ASN1_FUNCTIONS(SubjectKeyIdentifier);
+
+ASN1_SEQUENCE(TrustAnchorState) = {
+ ASN1_SEQUENCE_OF(TrustAnchorState, taskis, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TrustAnchorState, hash, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(TrustAnchorState);
+
+IMPLEMENT_ASN1_FUNCTIONS(TrustAnchorState);
+
+ASN1_ITEM_TEMPLATE(TASubjectKeyIdentifiers) =
+ ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, tas,
+ ASN1_OCTET_STRING)
+ASN1_ITEM_TEMPLATE_END(TASubjectKeyIdentifiers)
+
+IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(TASubjectKeyIdentifiers,
+ TASubjectKeyIdentifiers, TASubjectKeyIdentifiers);
+
+
static void
asn1int_set_seqnum(ASN1_INTEGER *aint, const char *seqnum)
{
@@ -456,6 +479,44 @@ generate_aspapayloadstate(struct validat
return vaps;
}
+static void
+append_ta(STACK_OF(SubjectKeyIdentifier) *taskis, struct ccr_taski *taski)
+{
+ SubjectKeyIdentifier *ski;
+
+ if ((ski = SubjectKeyIdentifier_new()) == NULL)
+ errx(1, "SubjectKeyIdentifier_new");
+
+ if (!ASN1_OCTET_STRING_set(ski, taski->keyid, sizeof(taski->keyid)))
+ errx(1, "ASN1_OCTET_STRING_set");
+
+ if ((sk_SubjectKeyIdentifier_push(taskis, ski)) <= 0)
+ errx(1, "sk_SubjectKeyIdentifier_push");
+}
+
+static TrustAnchorState *
+generate_trustanchorstate(struct validation_data *vd)
+{
+ TrustAnchorState *tas;
+ struct ccr_taski *taski;
+
+ if ((tas = TrustAnchorState_new()) == NULL)
+ errx(1, "TrustAnchorState_new");
+
+ RB_FOREACH(taski, ccr_taski_tree, &vd->ccr.taskis) {
+ append_ta(tas->taskis, taski);
+ }
+
+ hash_asn1_item(tas->hash, ASN1_ITEM_rptr(TASubjectKeyIdentifiers),
+ tas->taskis);
+
+ if (base64_encode(tas->hash->data, tas->hash->length,
+ &vd->ccr.tas_hash) == -1)
+ errx(1, "base64_encode");
+
+ return tas;
+}
+
static CanonicalCacheRepresentation *
generate_ccr(struct validation_data *vd)
{
@@ -481,6 +542,9 @@ generate_ccr(struct validation_data *vd)
if ((ccr->vaps = generate_aspapayloadstate(vd)) == NULL)
errx(1, "generate_aspapayloadstate");
+ if ((ccr->tas = generate_trustanchorstate(vd)) == NULL)
+ errx(1, "generate_trustanchorstate");
+
return ccr;
}
@@ -520,6 +584,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 07:29:12 -0000
@@ -476,12 +476,22 @@ 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;
+ char *tas_hash;
unsigned char *der;
size_t der_len;
};
@@ -1018,6 +1028,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 07:29:12 -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 07:29:12 -0000
@@ -53,6 +53,8 @@ extern ASN1_ITEM_EXP ROAPayloadSets_it;
extern ASN1_ITEM_EXP ROAPayloadSet_it;
extern ASN1_ITEM_EXP ASPAPayloadSets_it;
extern ASN1_ITEM_EXP ASPAPayloadSet_it;
+extern ASN1_ITEM_EXP TASubjectKeyIdentifiers_it;
+extern ASN1_ITEM_EXP SubjectKeyIdentifier_it;
typedef struct {
ASN1_OCTET_STRING *hash;
@@ -133,6 +135,28 @@ typedef struct {
DECLARE_ASN1_FUNCTIONS(ASPAPayloadState);
+typedef ASN1_OCTET_STRING SubjectKeyIdentifier;
+
+DECLARE_ASN1_FUNCTIONS(SubjectKeyIdentifier);
+
+DECLARE_STACK_OF(SubjectKeyIdentifier);
+
+#ifndef DEFINE_STACK_OF
+#define sk_SubjectKeyIdentifier_push(st, i) \
+ SKM_sk_push(SubjectKeyIdentifier, (st), (i))
+#endif
+
+typedef STACK_OF(SubjectKeyIdentifier) TASubjectKeyIdentifiers;
+
+DECLARE_ASN1_FUNCTIONS(TASubjectKeyIdentifiers);
+
+typedef struct {
+ STACK_OF(SubjectKeyIdentifier) *taskis;
+ ASN1_OCTET_STRING *hash;
+} TrustAnchorState;
+
+DECLARE_ASN1_FUNCTIONS(TrustAnchorState);
+
typedef struct {
ASN1_INTEGER *version;
ASN1_OBJECT *hashAlg;
@@ -140,6 +164,7 @@ typedef struct {
ManifestState *mfts;
ROAPayloadState *vrps;
ASPAPayloadState *vaps;
+ TrustAnchorState *tas;
} CanonicalCacheRepresentation;
DECLARE_ASN1_FUNCTIONS(CanonicalCacheRepresentation);
rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output
rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output
rpki-client: encode Trust Anchor SubjectKeyIdentifiers into CCR output