Index | Thread | Search

From:
Theo Buehler <tb@theobuehler.org>
Subject:
rpki-client: introduce and use signed_object_parse()
To:
tech@openbsd.org
Date:
Thu, 3 Sep 2026 15:39:41 +0200

Download raw body.

Thread
This refactoring diff does two simple things:

Hang an ->oid() getter off struct signed obj. Since foo_oid is populated
at runtime it's not a compile time constant, so we can't add it directly
to the static structs.

The six copies of foo_parse() get replaced by signed_object_parse() and
for now we pass in the rtype and use that to resolve the signed object
handler which is only used in there. cms_parse_validate() can be made
static and regress get a similar adjustment as well.

There is one subtle difference between the foo_parse() copies that
I should call out: mft and tak check the result of *_validate()
while aspa, roa, rsp, and spl do not. The latter always succeed, so
the switch from (void)foo_validate() to !sobj->validate() does not
change anything.

The .rtype member is currently unused. I chose to leave it there for now.

Index: usr.sbin/rpki-client/aspa.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/aspa.c,v
diff -u -p -r1.44 aspa.c
--- usr.sbin/rpki-client/aspa.c	25 Jun 2026 07:51:58 -0000	1.44
+++ usr.sbin/rpki-client/aspa.c	3 Sep 2026 12:46:09 -0000
@@ -179,6 +179,12 @@ aspa_validate(const char *fn, void *obj,
 	return 1; /* XXX */
 }
 
+static const ASN1_OBJECT *
+aspa_obj_oid(void)
+{
+	return aspa_oid;
+}
+
 static void *
 aspa_obj_new(size_t der_len, time_t signtime)
 {
@@ -199,60 +205,20 @@ aspa_obj_free(void *obj)
 
 static const struct signed_obj aspa_signed_obj = {
 	.rtype = RTYPE_ASPA,
+
 	.new = aspa_obj_new,
 	.free = aspa_obj_free,
 	.cert_info = aspa_cert_info,
 	.parse_econtent = aspa_parse_econtent,
 	.validate = aspa_validate,
+
+	.oid = aspa_obj_oid,
 };
 
 const struct signed_obj *
 aspa_obj(void)
 {
 	return &aspa_signed_obj;
-}
-
-/*
- * Parse a full ASPA file.
- * Returns the payload or NULL if the file was malformed.
- */
-struct aspa *
-aspa_parse(struct cert **out_cert, const char *fn, int talid,
-    const unsigned char *der, size_t len)
-{
-	struct aspa	*aspa;
-	struct cert	*cert = NULL;
-	size_t		 cmsz;
-	unsigned char	*cms;
-	time_t		 signtime = 0;
-	int		 rc = 0;
-
-	assert(*out_cert == NULL);
-
-	cms = cms_parse_validate(&cert, fn, talid, der, len, aspa_oid, &cmsz,
-	    &signtime);
-	if (cms == NULL)
-		return NULL;
-
-	aspa = aspa_obj_new(len, signtime);
-	if (!aspa_cert_info(fn, aspa, cert))
-		goto out;
-	if (!aspa_parse_econtent(fn, aspa, cms, cmsz))
-		goto out;
-	(void)aspa_validate(fn, aspa, cert);
-
-	*out_cert = cert;
-	cert = NULL;
-
-	rc = 1;
- out:
-	if (rc == 0) {
-		aspa_free(aspa);
-		aspa = NULL;
-	}
-	cert_free(cert);
-	free(cms);
-	return aspa;
 }
 
 /*
Index: usr.sbin/rpki-client/cms.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cms.c,v
diff -u -p -r1.61 cms.c
--- usr.sbin/rpki-client/cms.c	26 Aug 2026 05:57:47 -0000	1.61
+++ usr.sbin/rpki-client/cms.c	3 Sep 2026 13:30:54 -0000
@@ -401,7 +401,7 @@ cms_parse_validate_internal(struct cert 
  * The eContentType of the message must be an oid object.
  * Return the eContent as a string and set "rsz" to be its length.
  */
-unsigned char *
+static unsigned char *
 cms_parse_validate(struct cert **out_cert, const char *fn, int talid,
     const unsigned char *der, size_t derlen, const ASN1_OBJECT *oid,
     size_t *rsz, time_t *st)
@@ -413,4 +413,71 @@ cms_parse_validate(struct cert **out_cer
 		return NULL;
 
 	return res;
+}
+
+static const struct signed_obj *
+cms_object_from_rtype(const char *fn, enum rtype rtype)
+{
+	switch (rtype) {
+	case RTYPE_ASPA:
+		return aspa_obj();
+	case RTYPE_MFT:
+		return mft_obj();
+	case RTYPE_ROA:
+		return roa_obj();
+	case RTYPE_RSC:
+		return rsc_obj();
+	case RTYPE_SPL:
+		return spl_obj();
+	case RTYPE_TAK:
+		return tak_obj();
+	default:
+		errx(1, "%s: unsupported signed object", fn);
+	}
+}
+
+void *
+signed_object_parse(struct cert **out_cert, const char *fn, enum rtype rtype,
+    int talid, const unsigned char *der, size_t len)
+{
+	const struct signed_obj *sobj;
+	const ASN1_OBJECT *oid;
+	void *obj = NULL;
+	struct cert *cert = NULL;
+	size_t cmsz;
+	unsigned char *cms;
+	time_t signtime = 0;
+	int rc = 0;
+
+	assert(*out_cert == NULL);
+
+	sobj = cms_object_from_rtype(fn, rtype);
+	oid = sobj->oid();
+
+	cms = cms_parse_validate(&cert, fn, talid, der, len, oid, &cmsz,
+	    &signtime);
+	if (cms == NULL)
+		return NULL;
+
+	obj = sobj->new(len, signtime);
+	if (!sobj->cert_info(fn, obj, cert))
+		goto out;
+	if (!sobj->parse_econtent(fn, obj, cms, cmsz))
+		goto out;
+	if (!sobj->validate(fn, obj, cert))
+		goto out;
+
+	*out_cert = cert;
+	cert = NULL;
+
+	rc = 1;
+
+ out:
+	if (rc == 0) {
+		sobj->free(obj);
+		obj = NULL;
+	}
+	cert_free(cert);
+	free(cms);
+	return obj;
 }
Index: usr.sbin/rpki-client/extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
diff -u -p -r1.291 extern.h
--- usr.sbin/rpki-client/extern.h	25 Aug 2026 08:41:14 -0000	1.291
+++ usr.sbin/rpki-client/extern.h	3 Sep 2026 13:04:51 -0000
@@ -244,6 +244,7 @@ enum location {
 
 struct signed_obj {
 	enum rtype rtype;
+
 	void *(*new)(size_t, time_t);
 	void (*free)(void *);
 	int (*cert_info)(const char *, void *, const struct cert *);
@@ -251,6 +252,8 @@ struct signed_obj {
 	int (*parse_detached)(const char *, void *, BIO *, char *, size_t,
 	    uint8_t **, size_t *);
 	int (*validate)(const char *, void *, struct cert *);
+
+	const ASN1_OBJECT *(*oid)(void);
 };
 
 /*
@@ -776,8 +779,6 @@ void		 nca_tree_remove_cert(struct nca_t
 enum rtype	 rtype_from_file_extension(const char *);
 void		 mft_buffer(struct ibuf *, const struct mft *);
 void		 mft_free(struct mft *);
-struct mft	*mft_parse(struct cert **, const char *, int,
-		    const unsigned char *, size_t);
 const struct signed_obj *mft_obj(void);
 struct mft	*mft_read(struct ibuf *);
 int		 mft_compare_issued(const struct mft *, const struct mft *);
@@ -787,8 +788,6 @@ int		 mft_seqnum_gap_present(const struc
 
 void		 roa_buffer(struct ibuf *, const struct roa *);
 void		 roa_free(struct roa *);
-struct roa	*roa_parse(struct cert **, const char *, int,
-		    const unsigned char *, size_t);
 const struct signed_obj *roa_obj(void);
 struct roa	*roa_read(struct ibuf *);
 void		 roa_insert_vrps(struct vrp_tree *, struct roa *,
@@ -796,30 +795,22 @@ void		 roa_insert_vrps(struct vrp_tree *
 
 void		 spl_buffer(struct ibuf *, const struct spl *);
 void		 spl_free(struct spl *);
-struct spl	*spl_parse(struct cert **, const char *, int,
-		    const unsigned char *, size_t);
 const struct signed_obj *spl_obj(void);
 struct spl	*spl_read(struct ibuf *);
 void		 spl_insert_vsps(struct vsp_tree *, struct spl *,
 		    struct repo *);
 
 void		 rsc_free(struct rsc *);
-struct rsc	*rsc_parse(struct cert **, const char *, int,
-		    const unsigned char *, size_t);
 const struct signed_obj *rsc_obj(void);
 
 void		 takey_free(struct takey *);
 void		 tak_free(struct tak *);
-struct tak	*tak_parse(struct cert **, const char *, int,
-		    const unsigned char *, size_t);
 const struct signed_obj *tak_obj(void);
 
 void		 aspa_buffer(struct ibuf *, const struct aspa *);
 void		 aspa_free(struct aspa *);
 void		 aspa_insert_vaps(char *, struct vap_tree *, struct aspa *,
 		    struct repo *);
-struct aspa	*aspa_parse(struct cert **, const char *, int,
-		    const unsigned char *, size_t);
 const struct signed_obj *aspa_obj(void);
 struct aspa	*aspa_read(struct ibuf *);
 
@@ -849,9 +840,8 @@ int		 valid_uuid(const char *);
 int		 valid_spl(const char *, struct cert *, struct spl *);
 
 /* Working with CMS. */
-unsigned char	*cms_parse_validate(struct cert **, const char *, int,
-		    const unsigned char *, size_t, const ASN1_OBJECT *,
-		    size_t *, time_t *);
+void		*signed_object_parse(struct cert **, const char *, enum rtype,
+		 int, const unsigned char *, size_t);
 
 /* Work with RFC 3779 IP addresses, prefixes, ranges. */
 
Index: usr.sbin/rpki-client/filemode.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/filemode.c,v
diff -u -p -r1.86 filemode.c
--- usr.sbin/rpki-client/filemode.c	9 Jul 2026 11:39:19 -0000	1.86
+++ usr.sbin/rpki-client/filemode.c	3 Sep 2026 12:46:09 -0000
@@ -500,7 +500,7 @@ proc_parser_file(char *file, unsigned ch
 
 	switch (type) {
 	case RTYPE_ASPA:
-		aspa = aspa_parse(&cert, file, -1, buf, len);
+		aspa = signed_object_parse(&cert, file, type, -1, buf, len);
 		if (aspa == NULL)
 			break;
 		aia = cert->aia;
@@ -531,7 +531,7 @@ proc_parser_file(char *file, unsigned ch
 		crl_print(crl);
 		break;
 	case RTYPE_MFT:
-		mft = mft_parse(&cert, file, -1, buf, len);
+		mft = signed_object_parse(&cert, file, type, -1, buf, len);
 		if (mft == NULL)
 			break;
 		aia = cert->aia;
@@ -540,7 +540,7 @@ proc_parser_file(char *file, unsigned ch
 		notafter = &mft->nextupdate;
 		break;
 	case RTYPE_ROA:
-		roa = roa_parse(&cert, file, -1, buf, len);
+		roa = signed_object_parse(&cert, file, type, -1, buf, len);
 		if (roa == NULL)
 			break;
 		aia = cert->aia;
@@ -549,7 +549,7 @@ proc_parser_file(char *file, unsigned ch
 		notafter = &cert->notafter;
 		break;
 	case RTYPE_RSC:
-		rsc = rsc_parse(&cert, file, -1, buf, len);
+		rsc = signed_object_parse(&cert, file, type, -1, buf, len);
 		if (rsc == NULL)
 			break;
 		aia = cert->aia;
@@ -558,7 +558,7 @@ proc_parser_file(char *file, unsigned ch
 		notafter = &cert->notafter;
 		break;
 	case RTYPE_SPL:
-		spl = spl_parse(&cert, file, -1, buf, len);
+		spl = signed_object_parse(&cert, file, type, -1, buf, len);
 		if (spl == NULL)
 			break;
 		aia = cert->aia;
@@ -567,7 +567,7 @@ proc_parser_file(char *file, unsigned ch
 		notafter = &cert->notafter;
 		break;
 	case RTYPE_TAK:
-		tak = tak_parse(&cert, file, -1, buf, len);
+		tak = signed_object_parse(&cert, file, type, -1, buf, len);
 		if (tak == NULL)
 			break;
 		aia = cert->aia;
Index: usr.sbin/rpki-client/mft.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/mft.c,v
diff -u -p -r1.141 mft.c
--- usr.sbin/rpki-client/mft.c	25 Jun 2026 07:51:58 -0000	1.141
+++ usr.sbin/rpki-client/mft.c	3 Sep 2026 12:46:09 -0000
@@ -424,6 +424,12 @@ mft_validate(const char *fn, void *obj, 
 	return 1;
 }
 
+static const ASN1_OBJECT *
+mft_obj_oid(void)
+{
+	return mft_oid;
+}
+
 static void *
 mft_obj_new(size_t der_len, time_t signtime)
 {
@@ -445,61 +451,20 @@ mft_obj_free(void *obj)
 
 static const struct signed_obj mft_signed_obj = {
 	.rtype = RTYPE_MFT,
+
 	.new = mft_obj_new,
 	.free = mft_obj_free,
 	.cert_info = mft_cert_info,
 	.parse_econtent = mft_parse_econtent,
 	.validate = mft_validate,
+
+	.oid = mft_obj_oid,
 };
 
 const struct signed_obj *
 mft_obj(void)
 {
 	return &mft_signed_obj;
-}
-
-/*
- * Parse the objects that have been published in the manifest.
- * Return mft if it conforms to RFC 9286, otherwise NULL.
- */
-struct mft *
-mft_parse(struct cert **out_cert, const char *fn, int talid,
-    const unsigned char *der, size_t len)
-{
-	struct mft	*mft;
-	struct cert	*cert = NULL;
-	int		 rc = 0;
-	size_t		 cmsz;
-	unsigned char	*cms;
-	time_t		 signtime = 0;
-
-	assert(*out_cert == NULL);
-
-	cms = cms_parse_validate(&cert, fn, talid, der, len, mft_oid, &cmsz,
-	    &signtime);
-	if (cms == NULL)
-		return NULL;
-
-	mft = mft_obj_new(len, signtime);
-	if (!mft_cert_info(fn, mft, cert))
-		goto out;
-	if (!mft_parse_econtent(fn, mft, cms, cmsz))
-		goto out;
-	if (!mft_validate(fn, mft, cert))
-		goto out;
-
-	*out_cert = cert;
-	cert = NULL;
-
-	rc = 1;
- out:
-	if (rc == 0) {
-		mft_free(mft);
-		mft = NULL;
-	}
-	cert_free(cert);
-	free(cms);
-	return mft;
 }
 
 /*
Index: usr.sbin/rpki-client/parser.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/parser.c,v
diff -u -p -r1.183 parser.c
--- usr.sbin/rpki-client/parser.c	21 Jul 2026 12:21:42 -0000	1.183
+++ usr.sbin/rpki-client/parser.c	3 Sep 2026 12:56:08 -0000
@@ -211,7 +211,8 @@ proc_parser_roa(char *file, const unsign
 	struct crl		*crl;
 	const char		*errstr;
 
-	if ((roa = roa_parse(&cert, file, entp->talid, der, len)) == NULL)
+	if ((roa = signed_object_parse(&cert, file, RTYPE_ROA, entp->talid,
+	    der, len)) == NULL)
 		goto out;
 
 	a = find_issuer(file, entp->certid, cert->aki, entp->mftaki);
@@ -252,7 +253,8 @@ proc_parser_spl(char *file, const unsign
 	struct crl		*crl;
 	const char		*errstr;
 
-	if ((spl = spl_parse(&cert, file, entp->talid, der, len)) == NULL)
+	if ((spl = signed_object_parse(&cert, file, RTYPE_SPL, entp->talid,
+	    der, len)) == NULL)
 		goto out;
 
 	a = find_issuer(file, entp->certid, cert->aki, entp->mftaki);
@@ -412,7 +414,8 @@ proc_parser_mft_pre(struct entity *entp,
 	if (der == NULL && errno != ENOENT)
 		warn("parse file %s", file);
 
-	if ((mft = mft_parse(&cert, file, entp->talid, der, len)) == NULL) {
+	if ((mft = signed_object_parse(&cert, file, RTYPE_MFT, entp->talid,
+	    der, len)) == NULL) {
 		free(der);
 		return NULL;
 	}
@@ -772,7 +775,8 @@ proc_parser_aspa(char *file, const unsig
 	struct crl	*crl;
 	const char	*errstr;
 
-	if ((aspa = aspa_parse(&cert, file, entp->talid, der, len)) == NULL)
+	if ((aspa = signed_object_parse(&cert, file, RTYPE_ASPA, entp->talid,
+	    der, len)) == NULL)
 		goto out;
 
 	a = find_issuer(file, entp->certid, cert->aki, entp->mftaki);
@@ -812,7 +816,8 @@ proc_parser_tak(char *file, const unsign
 	struct auth	*a;
 	const char	*errstr;
 
-	if ((tak = tak_parse(&cert, file, entp->talid, der, len)) == NULL)
+	if ((tak = signed_object_parse(&cert, file, RTYPE_TAK, entp->talid,
+	    der, len)) == NULL)
 		goto out;
 
 	a = find_issuer(file, entp->certid, cert->aki, entp->mftaki);
Index: usr.sbin/rpki-client/roa.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/roa.c,v
diff -u -p -r1.90 roa.c
--- usr.sbin/rpki-client/roa.c	25 Jun 2026 07:51:58 -0000	1.90
+++ usr.sbin/rpki-client/roa.c	3 Sep 2026 12:46:09 -0000
@@ -236,6 +236,12 @@ roa_validate(const char *fn, void *obj, 
 	return 1; /* XXX */
 }
 
+static const ASN1_OBJECT *
+roa_obj_oid(void)
+{
+	return roa_oid;
+}
+
 static void *
 roa_obj_new(size_t der_len, time_t signtime)
 {
@@ -256,60 +262,20 @@ roa_obj_free(void *obj)
 
 static const struct signed_obj roa_signed_obj = {
 	.rtype = RTYPE_ROA,
+
 	.new = roa_obj_new,
 	.free = roa_obj_free,
 	.cert_info = roa_cert_info,
 	.parse_econtent = roa_parse_econtent,
 	.validate = roa_validate,
+
+	.oid = roa_obj_oid,
 };
 
 const struct signed_obj *
 roa_obj(void)
 {
 	return &roa_signed_obj;
-}
-
-/*
- * Parse a full RFC 9582 file.
- * Returns the ROA or NULL if the document was malformed.
- */
-struct roa *
-roa_parse(struct cert **out_cert, const char *fn, int talid,
-    const unsigned char *der, size_t len)
-{
-	struct roa	*roa;
-	struct cert	*cert = NULL;
-	size_t		 cmsz;
-	unsigned char	*cms;
-	time_t		 signtime = 0;
-	int		 rc = 0;
-
-	assert(*out_cert == NULL);
-
-	cms = cms_parse_validate(&cert, fn, talid, der, len, roa_oid, &cmsz,
-	    &signtime);
-	if (cms == NULL)
-		return NULL;
-
-	roa = roa_obj_new(len, signtime);
-	if (!roa_cert_info(fn, roa, cert))
-		goto out;
-	if (!roa_parse_econtent(fn, roa, cms, cmsz))
-		goto out;
-	(void)roa_validate(fn, roa, cert);
-
-	*out_cert = cert;
-	cert = NULL;
-
-	rc = 1;
- out:
-	if (rc == 0) {
-		roa_free(roa);
-		roa = NULL;
-	}
-	cert_free(cert);
-	free(cms);
-	return roa;
 }
 
 /*
Index: usr.sbin/rpki-client/rsc.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/rsc.c,v
diff -u -p -r1.47 rsc.c
--- usr.sbin/rpki-client/rsc.c	9 Jul 2026 12:13:56 -0000	1.47
+++ usr.sbin/rpki-client/rsc.c	3 Sep 2026 12:46:09 -0000
@@ -366,6 +366,12 @@ rsc_validate(const char *fn, void *obj, 
 	return 1; /* XXX */
 }
 
+static const ASN1_OBJECT *
+rsc_obj_oid(void)
+{
+	return rsc_oid;
+}
+
 static void *
 rsc_obj_new(size_t der_len, time_t signtime)
 {
@@ -386,60 +392,20 @@ rsc_obj_free(void *obj)
 
 static const struct signed_obj rsc_signed_obj = {
 	.rtype = RTYPE_RSC,
+
 	.new = rsc_obj_new,
 	.free = rsc_obj_free,
 	.cert_info = rsc_cert_info,
 	.parse_econtent = rsc_parse_econtent,
 	.validate = rsc_validate,
+
+	.oid = rsc_obj_oid,
 };
 
 const struct signed_obj *
 rsc_obj(void)
 {
 	return &rsc_signed_obj;
-}
-
-/*
- * Parse a full RFC 9323 file.
- * Returns the RSC or NULL if the object was malformed.
- */
-struct rsc *
-rsc_parse(struct cert **out_cert, const char *fn, int talid,
-    const unsigned char *der, size_t len)
-{
-	struct rsc		*rsc;
-	struct cert		*cert = NULL;
-	unsigned char		*cms;
-	size_t			 cmsz;
-	time_t			 signtime = 0;
-	int			 rc = 0;
-
-	assert(*out_cert == NULL);
-
-	cms = cms_parse_validate(&cert, fn, talid, der, len, rsc_oid, &cmsz,
-	    &signtime);
-	if (cms == NULL)
-		return NULL;
-
-	rsc = rsc_obj_new(len, signtime);
-	if (!rsc_cert_info(fn, rsc, cert))
-		goto out;
-	if (!rsc_parse_econtent(fn, rsc, cms, cmsz))
-		goto out;
-	(void)rsc_validate(fn, rsc, cert);
-
-	*out_cert = cert;
-	cert = NULL;
-
-	rc = 1;
- out:
-	if (rc == 0) {
-		rsc_free(rsc);
-		rsc = NULL;
-	}
-	cert_free(cert);
-	free(cms);
-	return rsc;
 }
 
 /*
Index: usr.sbin/rpki-client/spl.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/spl.c,v
diff -u -p -r1.18 spl.c
--- usr.sbin/rpki-client/spl.c	25 Jun 2026 07:51:58 -0000	1.18
+++ usr.sbin/rpki-client/spl.c	3 Sep 2026 12:46:09 -0000
@@ -241,6 +241,12 @@ spl_validate(const char *fn, void *obj, 
 	return 1; /* XXX */
 }
 
+static const ASN1_OBJECT *
+spl_obj_oid(void)
+{
+	return spl_oid;
+}
+
 static void *
 spl_obj_new(size_t der_len, time_t signtime)
 {
@@ -261,60 +267,20 @@ spl_obj_free(void *obj)
 
 static const struct signed_obj spl_signed_obj = {
 	.rtype = RTYPE_SPL,
+
 	.new = spl_obj_new,
 	.free = spl_obj_free,
 	.cert_info = spl_cert_info,
 	.parse_econtent = spl_parse_econtent,
 	.validate = spl_validate,
+
+	.oid = spl_obj_oid,
 };
 
 const struct signed_obj *
 spl_obj(void)
 {
 	return &spl_signed_obj;
-}
-
-/*
- * Parse a full Signed Prefix List file.
- * Returns the SPL, or NULL if the object was malformed.
- */
-struct spl *
-spl_parse(struct cert **out_cert, const char *fn, int talid,
-    const unsigned char *der, size_t len)
-{
-	struct spl	*spl;
-	struct cert	*cert = NULL;
-	size_t		 cmsz;
-	unsigned char	*cms;
-	time_t		 signtime = 0;
-	int		 rc = 0;
-
-	assert(*out_cert == NULL);
-
-	cms = cms_parse_validate(&cert, fn, talid, der, len, spl_oid, &cmsz,
-	    &signtime);
-	if (cms == NULL)
-		return NULL;
-
-	spl = spl_obj_new(len, signtime);
-	if (!spl_cert_info(fn, spl, cert))
-		goto out;
-	if (!spl_parse_econtent(fn, spl, cms, cmsz))
-		goto out;
-	(void)spl_validate(fn, spl, cert);
-
-	*out_cert = cert;
-	cert = NULL;
-
-	rc = 1;
- out:
-	if (rc == 0) {
-		spl_free(spl);
-		spl = NULL;
-	}
-	cert_free(cert);
-	free(cms);
-	return spl;
 }
 
 void
Index: usr.sbin/rpki-client/tak.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/tak.c,v
diff -u -p -r1.32 tak.c
--- usr.sbin/rpki-client/tak.c	7 Jul 2026 13:45:59 -0000	1.32
+++ usr.sbin/rpki-client/tak.c	3 Sep 2026 12:46:09 -0000
@@ -208,6 +208,12 @@ tak_validate(const char *fn, void *obj, 
 	return 1;
 }
 
+static const ASN1_OBJECT *
+tak_obj_oid(void)
+{
+	return tak_oid;
+}
+
 static void *
 tak_obj_new(size_t der_len, time_t signtime)
 {
@@ -228,61 +234,20 @@ tak_obj_free(void *obj)
 
 static const struct signed_obj tak_signed_obj = {
 	.rtype = RTYPE_TAK,
+
 	.new = tak_obj_new,
 	.free = tak_obj_free,
 	.cert_info = tak_cert_info,
 	.parse_econtent = tak_parse_econtent,
 	.validate = tak_validate,
+
+	.oid = tak_obj_oid,
 };
 
 const struct signed_obj *
 tak_obj(void)
 {
 	return &tak_signed_obj;
-}
-
-/*
- * Parse a full RFC 9691 Trust Anchor Key file.
- * Returns the TAK or NULL if the object was malformed.
- */
-struct tak *
-tak_parse(struct cert **out_cert, const char *fn, int talid,
-    const unsigned char *der, size_t len)
-{
-	struct tak		*tak;
-	struct cert		*cert = NULL;
-	unsigned char		*cms;
-	size_t			 cmsz;
-	time_t			 signtime = 0;
-	int			 rc = 0;
-
-	assert(*out_cert == NULL);
-
-	cms = cms_parse_validate(&cert, fn, talid, der, len, tak_oid, &cmsz,
-	    &signtime);
-	if (cms == NULL)
-		return NULL;
-
-	tak = tak_obj_new(len, signtime);
-	if (!tak_cert_info(fn, tak, cert))
-		goto out;
-	if (!tak_parse_econtent(fn, tak, cms, cmsz))
-		goto out;
-	if (!tak_validate(fn, tak, cert))
-		goto out;
-
-	*out_cert = cert;
-	cert = NULL;
-
-	rc = 1;
- out:
-	if (rc == 0) {
-		tak_free(tak);
-		tak = NULL;
-	}
-	cert_free(cert);
-	free(cms);
-	return tak;
 }
 
 /*
Index: regress/usr.sbin/rpki-client/test-aspa.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/test-aspa.c,v
diff -u -p -r1.12 test-aspa.c
--- regress/usr.sbin/rpki-client/test-aspa.c	23 Oct 2025 05:35:46 -0000	1.12
+++ regress/usr.sbin/rpki-client/test-aspa.c	3 Sep 2026 12:46:09 -0000
@@ -63,7 +63,8 @@ main(int argc, char *argv[])
 
 	for (i = 0; i < argc; i++) {
 		buf = load_file(argv[i], &len);
-		if ((p = aspa_parse(&cert, argv[i], -1, buf, len)) == NULL) {
+		if ((p = signed_object_parse(&cert, argv[i], RTYPE_ASPA, -1,
+		    buf, len)) == NULL) {
 			free(buf);
 			break;
 		}
Index: regress/usr.sbin/rpki-client/test-mft.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/test-mft.c,v
diff -u -p -r1.33 test-mft.c
--- regress/usr.sbin/rpki-client/test-mft.c	23 Oct 2025 05:35:46 -0000	1.33
+++ regress/usr.sbin/rpki-client/test-mft.c	3 Sep 2026 12:46:09 -0000
@@ -65,7 +65,8 @@ main(int argc, char *argv[])
 
 	for (i = 0; i < argc; i++) {
 		buf = load_file(argv[i], &len);
-		if ((p = mft_parse(&cert, argv[i], -1, buf, len)) == NULL) {
+		if ((p = signed_object_parse(&cert, argv[i], RTYPE_MFT, -1,
+		    buf, len)) == NULL) {
 			free(buf);
 			break;
 		}
Index: regress/usr.sbin/rpki-client/test-roa.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/test-roa.c,v
diff -u -p -r1.30 test-roa.c
--- regress/usr.sbin/rpki-client/test-roa.c	23 Oct 2025 05:35:46 -0000	1.30
+++ regress/usr.sbin/rpki-client/test-roa.c	3 Sep 2026 12:46:09 -0000
@@ -62,7 +62,8 @@ main(int argc, char *argv[])
 
 	for (i = 0; i < argc; i++) {
 		buf = load_file(argv[i], &len);
-		if ((p = roa_parse(&cert, argv[i], -1, buf, len)) == NULL) {
+		if ((p = signed_object_parse(&cert, argv[i], RTYPE_ROA, -1,
+		    buf, len)) == NULL) {
 			free(buf);
 			break;
 		}
Index: regress/usr.sbin/rpki-client/test-rsc.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/test-rsc.c,v
diff -u -p -r1.15 test-rsc.c
--- regress/usr.sbin/rpki-client/test-rsc.c	23 Oct 2025 05:35:46 -0000	1.15
+++ regress/usr.sbin/rpki-client/test-rsc.c	3 Sep 2026 12:46:09 -0000
@@ -65,7 +65,8 @@ main(int argc, char *argv[])
 
 	for (i = 0; i < argc; i++) {
 		buf = load_file(argv[i], &len);
-		if ((p = rsc_parse(&cert, argv[i], -1, buf, len)) == NULL) {
+		if ((p = signed_object_parse(&cert, argv[i], RTYPE_RSC, -1,
+		    buf, len)) == NULL) {
 			free(buf);
 			break;
 		}
Index: regress/usr.sbin/rpki-client/test-spl.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/test-spl.c,v
diff -u -p -r1.7 test-spl.c
--- regress/usr.sbin/rpki-client/test-spl.c	23 Oct 2025 05:35:46 -0000	1.7
+++ regress/usr.sbin/rpki-client/test-spl.c	3 Sep 2026 12:46:09 -0000
@@ -63,7 +63,8 @@ main(int argc, char *argv[])
 
 	for (i = 0; i < argc; i++) {
 		buf = load_file(argv[i], &len);
-		if ((p = spl_parse(&cert, argv[i], -1, buf, len)) == NULL) {
+		if ((p = signed_object_parse(&cert, argv[i], RTYPE_SPL,
+		    -1, buf, len)) == NULL) {
 			free(buf);
 			break;
 		}
Index: regress/usr.sbin/rpki-client/test-tak.c
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/test-tak.c,v
diff -u -p -r1.12 test-tak.c
--- regress/usr.sbin/rpki-client/test-tak.c	23 Oct 2025 05:35:46 -0000	1.12
+++ regress/usr.sbin/rpki-client/test-tak.c	3 Sep 2026 12:46:09 -0000
@@ -63,7 +63,8 @@ main(int argc, char *argv[])
 
 	for (i = 0; i < argc; i++) {
 		buf = load_file(argv[i], &len);
-		if ((p = tak_parse(&cert, argv[i], -1, buf, len)) == NULL) {
+		if ((p = signed_object_parse(&cert, argv[i], RTYPE_TAK, -1,
+		    buf, len)) == NULL) {
 			free(buf);
 			break;
 		}