I chose to implement the constrained versions of the RFC 3779 types from
the draft because the OpenSSL RFC 3779 code has static IPAddrBlocks_it,
so we have to work around that anyway. This isn't quite minimal, but it
avoids asymmetry between ASIdentifiers and IPAddrBlocks and it's cleaner
than reusing as many of the available RFC 3779 types as possible (which
also means additional checks either when walking the structs or after).

The diff has three parts that build on top of each other. There is no
overlap outside of extern.h, so it should not make the review harder.

The mechanical cert.c diff adjusts some sbgp_addr_*() and sbgp_as_*() to
remove the struct parse argument so that we can use them from rsc.c.

The rsc.c diff is the tricky part: it switches to templates and uses the
cert.c functions. rsc_parse_aslist() and rsc_parse_iplist() are similar
to sbgp_assysnum() and sbgp_ipaddrblk(), but somewhat easier. We get
rid of the copy-paste XXXs and the last bit of low level ASN.1 fiddling. 

Remove the unused ASN1_frame() and cms_econtent_version() from cms.c.

Index: cert.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v
retrieving revision 1.82
diff -u -p -r1.82 cert.c
--- cert.c      15 May 2022 15:00:53 -0000      1.82
+++ cert.c      31 May 2022 10:46:00 -0000
@@ -58,11 +58,12 @@ extern ASN1_OBJECT  *notify_oid;    /* 1.3.6
  * Returns zero on failure (IP overlap) non-zero on success.
  */
 static int
-append_ip(struct parse *p, const struct cert_ip *ip)
+append_ip(const char *fn, struct cert_ip *ips, size_t *ipsz,
+    const struct cert_ip *ip)
 {
-       if (!ip_addr_check_overlap(ip, p->fn, p->res->ips, p->res->ipsz))
+       if (!ip_addr_check_overlap(ip, fn, ips, *ipsz))
                return 0;
-       p->res->ips[p->res->ipsz++] = *ip;
+       ips[(*ipsz)++] = *ip;
        return 1;
 }
 
@@ -72,11 +73,12 @@ append_ip(struct parse *p, const struct 
  * as defined by RFC 3779 section 3.3.
  */
 static int
-append_as(struct parse *p, const struct cert_as *as)
+append_as(const char *fn, struct cert_as *ases, size_t *asz,
+    const struct cert_as *as)
 {
-       if (!as_check_overlap(as, p->fn, p->res->as, p->res->asz))
+       if (!as_check_overlap(as, fn, ases, *asz))
                return 0;
-       p->res->as[p->res->asz++] = *as;
+       ases[(*asz)++] = *as;
        return 1;
 }
 
@@ -84,8 +86,9 @@ append_as(struct parse *p, const struct 
  * Parse a range of AS identifiers as in 3.2.3.8.
  * Returns zero on failure, non-zero on success.
  */
-static int
-sbgp_asrange(struct parse *p, const ASRange *range)
+int
+sbgp_as_range(const char *fn, struct cert_as *ases, size_t *asz,
+    const ASRange *range)
 {
        struct cert_as           as;
 
@@ -94,34 +97,35 @@ sbgp_asrange(struct parse *p, const ASRa
 
        if (!as_id_parse(range->min, &as.range.min)) {
                warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): "
-                   "malformed AS identifier", p->fn);
+                   "malformed AS identifier", fn);
                return 0;
        }
 
        if (!as_id_parse(range->max, &as.range.max)) {
                warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): "
-                   "malformed AS identifier", p->fn);
+                   "malformed AS identifier", fn);
                return 0;
        }
 
        if (as.range.max == as.range.min) {
                warnx("%s: RFC 3379 section 3.2.3.8: ASRange: "
-                   "range is singular", p->fn);
+                   "range is singular", fn);
                return 0;
        } else if (as.range.max < as.range.min) {
                warnx("%s: RFC 3379 section 3.2.3.8: ASRange: "
-                   "range is out of order", p->fn);
+                   "range is out of order", fn);
                return 0;
        }
 
-       return append_as(p, &as);
+       return append_as(fn, ases, asz, &as);
 }
 
 /*
  * Parse an entire 3.2.3.10 integer type.
  */
-static int
-sbgp_asid(struct parse *p, const ASN1_INTEGER *i)
+int
+sbgp_as_id(const char *fn, struct cert_as *ases, size_t *asz,
+    const ASN1_INTEGER *i)
 {
        struct cert_as   as;
 
@@ -130,27 +134,27 @@ sbgp_asid(struct parse *p, const ASN1_IN
 
        if (!as_id_parse(i, &as.id)) {
                warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): "
-                   "malformed AS identifier", p->fn);
+                   "malformed AS identifier", fn);
                return 0;
        }
        if (as.id == 0) {
                warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): "
-                   "AS identifier zero is reserved", p->fn);
+                   "AS identifier zero is reserved", fn);
                return 0;
        }
 
-       return append_as(p, &as);
+       return append_as(fn, ases, asz, &as);
 }
 
 static int
-sbgp_asinherit(struct parse *p)
+sbgp_as_inherit(const char *fn, struct cert_as *ases, size_t *asz)
 {
        struct cert_as as;
 
        memset(&as, 0, sizeof(struct cert_as));
        as.type = CERT_AS_INHERIT;
 
-       return append_as(p, &as);
+       return append_as(fn, ases, asz, &as);
 }
 
 /*
@@ -214,7 +218,7 @@ sbgp_assysnum(struct parse *p, X509_EXTE
                err(1, NULL);
 
        if (aors == NULL) {
-               if (!sbgp_asinherit(p))
+               if (!sbgp_as_inherit(p->fn, p->res->as, &p->res->asz))
                        goto out;
        }
 
@@ -224,11 +228,13 @@ sbgp_assysnum(struct parse *p, X509_EXTE
                aor = sk_ASIdOrRange_value(aors, i);
                switch (aor->type) {
                case ASIdOrRange_id:
-                       if (!sbgp_asid(p, aor->u.id))
+                       if (!sbgp_as_id(p->fn, p->res->as, &p->res->asz,
+                           aor->u.id))
                                goto out;
                        break;
                case ASIdOrRange_range:
-                       if (!sbgp_asrange(p, aor->u.range))
+                       if (!sbgp_as_range(p->fn, p->res->as, &p->res->asz,
+                           aor->u.range))
                                goto out;
                        break;
                default:
@@ -248,8 +254,9 @@ sbgp_assysnum(struct parse *p, X509_EXTE
  * Construct a RFC 3779 2.2.3.8 range from its bit string.
  * Returns zero on failure, non-zero on success.
  */
-static int
-sbgp_addr(struct parse *p, enum afi afi, const ASN1_BIT_STRING *bs)
+int
+sbgp_addr(const char *fn, struct cert_ip *ips, size_t *ipsz, enum afi afi,
+    const ASN1_BIT_STRING *bs)
 {
        struct cert_ip  ip;
 
@@ -258,27 +265,28 @@ sbgp_addr(struct parse *p, enum afi afi,
        ip.afi = afi;
        ip.type = CERT_IP_ADDR;
 
-       if (!ip_addr_parse(bs, afi, p->fn, &ip.ip)) {
+       if (!ip_addr_parse(bs, afi, fn, &ip.ip)) {
                warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: "
-                   "invalid IP address", p->fn);
+                   "invalid IP address", fn);
                return 0;
        }
 
        if (!ip_cert_compose_ranges(&ip)) {
                warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: "
-                   "IP address range reversed", p->fn);
+                   "IP address range reversed", fn);
                return 0;
        }
 
-       return append_ip(p, &ip);
+       return append_ip(fn, ips, ipsz, &ip);
 }
 
 /*
  * Parse RFC 3779 2.2.3.9 range of addresses.
  * Returns zero on failure, non-zero on success.
  */
-static int
-sbgp_addr_range(struct parse *p, enum afi afi, const IPAddressRange *range)
+int
+sbgp_addr_range(const char *fn, struct cert_ip *ips, size_t *ipsz,
+    enum afi afi, const IPAddressRange *range)
 {
        struct cert_ip  ip;
 
@@ -287,29 +295,30 @@ sbgp_addr_range(struct parse *p, enum af
        ip.afi = afi;
        ip.type = CERT_IP_RANGE;
 
-       if (!ip_addr_parse(range->min, afi, p->fn, &ip.range.min)) {
+       if (!ip_addr_parse(range->min, afi, fn, &ip.range.min)) {
                warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
-                   "invalid IP address", p->fn);
+                   "invalid IP address", fn);
                return 0;
        }
 
-       if (!ip_addr_parse(range->max, afi, p->fn, &ip.range.max)) {
+       if (!ip_addr_parse(range->max, afi, fn, &ip.range.max)) {
                warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
-                   "invalid IP address", p->fn);
+                   "invalid IP address", fn);
                return 0;
        }
 
        if (!ip_cert_compose_ranges(&ip)) {
                warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
-                   "IP address range reversed", p->fn);
+                   "IP address range reversed", fn);
                return 0;
        }
 
-       return append_ip(p, &ip);
+       return append_ip(fn, ips, ipsz, &ip);
 }
 
 static int
-sbgp_addr_inherit(struct parse *p, enum afi afi)
+sbgp_addr_inherit(const char *fn, struct cert_ip *ips, size_t *ipsz,
+    enum afi afi)
 {
        struct cert_ip  ip;
 
@@ -318,7 +327,7 @@ sbgp_addr_inherit(struct parse *p, enum 
        ip.afi = afi;
        ip.type = CERT_IP_INHERIT;
 
-       return append_ip(p, &ip);
+       return append_ip(fn, ips, ipsz, &ip);
 }
 
 /*
@@ -380,7 +389,8 @@ sbgp_ipaddrblk(struct parse *p, X509_EXT
                }
 
                if (aors == NULL) {
-                       if (!sbgp_addr_inherit(p, afi))
+                       if (!sbgp_addr_inherit(p->fn, p->res->ips,
+                           &p->res->ipsz, afi))
                                goto out;
                        continue;
                }
@@ -389,12 +399,13 @@ sbgp_ipaddrblk(struct parse *p, X509_EXT
                        aor = sk_IPAddressOrRange_value(aors, j);
                        switch (aor->type) {
                        case IPAddressOrRange_addressPrefix:
-                               if (!sbgp_addr(p, afi, aor->u.addressPrefix))
+                               if (!sbgp_addr(p->fn, p->res->ips,
+                                   &p->res->ipsz, afi, aor->u.addressPrefix))
                                        goto out;
                                break;
                        case IPAddressOrRange_addressRange:
-                               if (!sbgp_addr_range(p, afi,
-                                   aor->u.addressRange))
+                               if (!sbgp_addr_range(p->fn, p->res->ips,
+                                   &p->res->ipsz, afi, aor->u.addressRange))
                                        goto out;
                                break;
                        default:
Index: cms.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cms.c,v
retrieving revision 1.19
diff -u -p -r1.19 cms.c
--- cms.c       15 May 2022 16:43:34 -0000      1.19
+++ cms.c       28 May 2022 09:04:10 -0000
@@ -272,61 +272,3 @@ out:
 
        return res;
 }
-
-/*
- * Wrapper around ASN1_get_object() that preserves the current start
- * state and returns a more meaningful value.
- * Return zero on failure, non-zero on success.
- */
-int
-ASN1_frame(const char *fn, size_t sz,
-    const unsigned char **cnt, long *cntsz, int *tag)
-{
-       int      ret, pcls;
-
-       ret = ASN1_get_object(cnt, cntsz, tag, &pcls, sz);
-       if ((ret & 0x80)) {
-               cryptowarnx("%s: ASN1_get_object", fn);
-               return 0;
-       }
-       return ASN1_object_size((ret & 0x01) ? 2 : 0, *cntsz, *tag);
-}
-
-/*
- * Check the version field in eContent.
- * Returns -1 on failure, zero on success.
- */
-int
-cms_econtent_version(const char *fn, const unsigned char **d, size_t dsz,
-    long *version)
-{
-       ASN1_INTEGER    *aint = NULL;
-       long             plen;
-       int              ptag, rc = -1;
-
-       if (!ASN1_frame(fn, dsz, d, &plen, &ptag))
-               goto out;
-       if (ptag != 0) {
-               warnx("%s: eContent version: expected explicit tag [0]", fn);
-               goto out;
-       }
-
-       aint = d2i_ASN1_INTEGER(NULL, d, plen);
-       if (aint == NULL) {
-               cryptowarnx("%s: eContent version: failed d2i_ASN1_INTEGER",
-                   fn);
-               goto out;
-       }
-
-       *version = ASN1_INTEGER_get(aint);
-       if (*version < 0) {
-               warnx("%s: eContent version: expected positive integer, got:"
-                   " %ld", fn, *version);
-               goto out;
-       }
-
-       rc = 0;
-out:
-       ASN1_INTEGER_free(aint);
-       return rc;
-}
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
retrieving revision 1.138
diff -u -p -r1.138 extern.h
--- extern.h    24 May 2022 09:20:49 -0000      1.138
+++ extern.h    29 May 2022 21:05:03 -0000
@@ -513,11 +513,6 @@ int                 valid_rsc(const char *, struct aut
 unsigned char  *cms_parse_validate(X509 **, const char *,
                    const unsigned char *, size_t,
                    const ASN1_OBJECT *, size_t *);
-int             cms_econtent_version(const char *, const unsigned char **,
-                   size_t, long *);
-/* Helper for ASN1 parsing */
-int             ASN1_frame(const char *, size_t,
-                       const unsigned char **, long *, int *);
 
 /* Work with RFC 3779 IP addresses, prefixes, ranges. */
 
@@ -535,6 +530,11 @@ int                 ip_addr_check_covered(enum afi, co
 int             ip_cert_compose_ranges(struct cert_ip *);
 void            ip_roa_compose_ranges(struct roa_ip *);
 
+int             sbgp_addr(const char *, struct cert_ip *, size_t *,
+                   enum afi, const ASN1_BIT_STRING *);
+int             sbgp_addr_range(const char *, struct cert_ip *, size_t *,
+                   enum afi, const IPAddressRange *);
+
 /* Work with RFC 3779 AS numbers, ranges. */
 
 int             as_id_parse(const ASN1_INTEGER *, uint32_t *);
@@ -542,6 +542,11 @@ int                 as_check_overlap(const struct cert
                        const struct cert_as *, size_t);
 int             as_check_covered(uint32_t, uint32_t,
                        const struct cert_as *, size_t);
+
+int             sbgp_as_id(const char *, struct cert_as *, size_t *,
+                   const ASN1_INTEGER *);
+int             sbgp_as_range(const char *, struct cert_as *, size_t *,
+                   const ASRange *);
 
 /* Parser-specific */
 void            entity_free(struct entity *);
Index: rpki-client.8
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/rpki-client.8,v
retrieving revision 1.64
diff -u -p -r1.64 rpki-client.8
--- rpki-client.8       20 May 2022 10:38:39 -0000      1.64
+++ rpki-client.8       28 May 2022 09:04:10 -0000
@@ -271,7 +271,7 @@ A Profile for BGPsec Router Certificates
 Certification Requests.
 .It RFC 8630
 Resource Public Key Infrastructure (RPKI) Trust Anchor Locator.
-.It draft-ietf-sidrops-rpki-rsc-06
+.It draft-ietf-sidrops-rpki-rsc-08
 A profile for Resource Public Key Infrastructure (RPKI) Signed Checklists 
(RSC).
 .El
 .Sh HISTORY
Index: rsc.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/rsc.c,v
retrieving revision 1.4
diff -u -p -r1.4 rsc.c
--- rsc.c       15 May 2022 16:43:35 -0000      1.4
+++ rsc.c       31 May 2022 11:09:26 -0000
@@ -16,16 +16,17 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <assert.h>
 #include <err.h>
-#include <stdarg.h>
-#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/safestack.h>
+#include <openssl/stack.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 
 #include "extern.h"
 
@@ -40,520 +41,273 @@ struct    parse {
 extern ASN1_OBJECT     *rsc_oid;
 
 /*
- * Append an AS identifier structure to our list of results.
- * Return zero on failure.
- * XXX: merge with append_as() in cert.c
+ * Types and templates for RSC eContent draft-ietf-sidrops-rpki-rsc-08
  */
-static int
-append_as(struct parse *p, const struct cert_as *as)
-{
-       if (!as_check_overlap(as, p->fn, p->res->as, p->res->asz))
-               return 0;
-       if (p->res->asz >= MAX_AS_SIZE)
-               return 0;
-       p->res->as = reallocarray(p->res->as, p->res->asz + 1,
-           sizeof(struct cert_as));
-       if (p->res->as == NULL)
-               err(1, NULL);
-       p->res->as[p->res->asz++] = *as;
-       return 1;
-}
 
-/*
- * Append an IP address structure to our list of results.
- * return zero on failure.
- * XXX: merge with append_ip() in cert.c
- */
-static int
-append_ip(struct parse *p, const struct cert_ip *ip)
-{
-       struct rsc      *res = p->res;
+typedef struct {
+       ASIdOrRanges            *asnum;
+} ConstrainedASIdentifiers;
+
+ASN1_SEQUENCE(ConstrainedASIdentifiers) = {
+       ASN1_EXP_SEQUENCE_OF(ConstrainedASIdentifiers, asnum, ASIdOrRange, 0),
+} ASN1_SEQUENCE_END(ConstrainedASIdentifiers);
+
+typedef struct {
+       ASN1_OCTET_STRING               *addressFamily;
+       STACK_OF(IPAddressOrRange)      *addressesOrRanges;
+} ConstrainedIPAddressFamily;
+
+ASN1_SEQUENCE(ConstrainedIPAddressFamily) = {
+       ASN1_SIMPLE(ConstrainedIPAddressFamily, addressFamily,
+           ASN1_OCTET_STRING),
+       ASN1_SEQUENCE_OF(ConstrainedIPAddressFamily, addressesOrRanges,
+           IPAddressOrRange),
+} ASN1_SEQUENCE_END(ConstrainedIPAddressFamily);
+
+typedef STACK_OF(ConstrainedIPAddressFamily) ConstrainedIPAddrBlocks;
+DECLARE_STACK_OF(ConstrainedIPAddressFamily);
+
+ASN1_ITEM_TEMPLATE(ConstrainedIPAddrBlocks) =
+       ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, ConstrainedIPAddrBlocks,
+           ConstrainedIPAddressFamily)
+ASN1_ITEM_TEMPLATE_END(ConstrainedIPAddrBlocks);
+
+typedef struct {
+       ConstrainedASIdentifiers        *asID;
+       ConstrainedIPAddrBlocks         *ipAddrBlocks;
+} ResourceBlock;
+
+ASN1_SEQUENCE(ResourceBlock) = {
+       ASN1_EXP_OPT(ResourceBlock, asID, ConstrainedASIdentifiers, 0),
+       ASN1_EXP_SEQUENCE_OF_OPT(ResourceBlock, ipAddrBlocks,
+           ConstrainedIPAddressFamily, 1)
+} ASN1_SEQUENCE_END(ResourceBlock);
+
+typedef struct {
+       ASN1_IA5STRING          *fileName;
+       ASN1_OCTET_STRING       *hash;
+} FileNameAndHash;
+
+DECLARE_STACK_OF(FileNameAndHash);
+
+#ifndef DEFINE_STACK_OF
+#define sk_ConstrainedIPAddressFamily_num(sk) \
+    SKM_sk_num(ConstrainedIPAddressFamily, (sk))
+#define sk_ConstrainedIPAddressFamily_value(sk, i) \
+    SKM_sk_value(ConstrainedIPAddressFamily, (sk), (i))
+
+#define sk_FileNameAndHash_num(sk)     SKM_sk_num(FileNameAndHash, (sk))
+#define sk_FileNameAndHash_value(sk, i)        SKM_sk_value(FileNameAndHash, 
(sk), (i))
+#endif
+
+ASN1_SEQUENCE(FileNameAndHash) = {
+       ASN1_OPT(FileNameAndHash, fileName, ASN1_IA5STRING),
+       ASN1_SIMPLE(FileNameAndHash, hash, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(FileNameAndHash);
+
+typedef struct {
+       ASN1_INTEGER                    *version;
+       ResourceBlock                   *resources;
+       X509_ALGOR                      *digestAlgorithm;
+       STACK_OF(FileNameAndHash)       *checkList;
+} RpkiSignedChecklist;
+
+ASN1_SEQUENCE(RpkiSignedChecklist) = {
+       ASN1_IMP_OPT(RpkiSignedChecklist, version, ASN1_INTEGER, 0),
+       ASN1_SIMPLE(RpkiSignedChecklist, resources, ResourceBlock),
+       ASN1_SIMPLE(RpkiSignedChecklist, digestAlgorithm, X509_ALGOR),
+       ASN1_SEQUENCE_OF(RpkiSignedChecklist, checkList, FileNameAndHash),
+} ASN1_SEQUENCE_END(RpkiSignedChecklist);
 
-       if (!ip_addr_check_overlap(ip, p->fn, p->res->ips, p->res->ipsz))
-               return 0;
-       if (res->ipsz >= MAX_IP_SIZE)
-               return 0;
-
-       res->ips = reallocarray(res->ips, res->ipsz + 1,
-           sizeof(struct cert_ip));
-       if (res->ips == NULL)
-               err(1, NULL);
-
-       res->ips[res->ipsz++] = *ip;
-       return 1;
-}
+DECLARE_ASN1_FUNCTIONS(RpkiSignedChecklist);
+IMPLEMENT_ASN1_FUNCTIONS(RpkiSignedChecklist);
 
 static int
-rsc_check_digesttype(struct parse *p, const unsigned char *d, size_t dsz)
+rsc_check_digesttype(struct parse *p, const X509_ALGOR *alg)
 {
-       X509_ALGOR              *alg;
        const ASN1_OBJECT       *obj;
        int                      type, nid;
-       int                      rc = 0;
-
-       if ((alg = d2i_X509_ALGOR(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC DigestAlgorithmIdentifier faild to parse",
-                   p->fn);
-               goto out;
-       }
 
        X509_ALGOR_get0(&obj, &type, NULL, alg);
 
        if (type != V_ASN1_UNDEF) {
                warnx("%s: RSC DigestAlgorithmIdentifier unexpected parameters:"
                    " %d", p->fn, type);
-               goto out;
+               return 0;
        }
 
        if ((nid = OBJ_obj2nid(obj)) != NID_sha256) {
                warnx("%s: RSC DigestAlgorithmIdentifier: want SHA256, have %s"
                    " (NID %d)", p->fn, ASN1_tag2str(nid), nid);
-               goto out;
+               return 0;
        }
 
-       rc = 1;
- out:
-       X509_ALGOR_free(alg);
-       return rc;
+       return 1;
 }
 
 /*
- * Parse and individual "FileNameAndHash", draft-ietf-sidrops-rpki-rsc
- * section 4.1.
+ * Parse the FileNameAndHash sequence, draft-ietf-sidrops-rpki-rsc, section 4.1
  * Return zero on failure, non-zero on success.
  */
 static int
-rsc_parse_filenamehash(struct parse *p, const ASN1_OCTET_STRING *os)
+rsc_parse_checklist(struct parse *p, const STACK_OF(FileNameAndHash) 
*checkList)
 {
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *file, *hash;
-       char                    *fn = NULL;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
-       int                      i = 0, rc = 0, elemsz;
-       struct rscfile          *rent;
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC FileNameAndHash: failed ASN.1 sequence "
-                   "parse", p->fn);
-               goto out;
-       }
-
-       elemsz = sk_ASN1_TYPE_num(seq);
-       if (elemsz != 1 && elemsz != 2) {
-               warnx("%s: RSC FileNameAndHash: want 1 or 2 elements, have %d",
-                   p->fn, elemsz);
-               goto out;
-       }
+       FileNameAndHash         *fh;
+       ASN1_IA5STRING          *fn;
+       struct rscfile          *file;
+       size_t                   sz, i;
 
-       if (elemsz == 2) {
-               ASN1_IA5STRING *filename;
-
-               file = sk_ASN1_TYPE_value(seq, i++);
-               if (file->type != V_ASN1_IA5STRING) {
-                       warnx("%s: RSC FileNameAndHash: want ASN.1 IA5 string,"
-                           " have %s (NID %d)", p->fn,
-                           ASN1_tag2str(file->type), file->type);
-                       goto out;
-               }
-
-               filename = file->value.ia5string;
-
-               if (!valid_filename(filename->data, filename->length)) {
-                       warnx("%s: RSC FileNameAndHash: bad filename", p->fn);
-                       goto out;
-               }
-
-               fn = strndup(filename->data, filename->length);
-               if (fn == NULL)
-                       err(1, NULL);
-       }
-
-       /* Now hash value. */
-
-       hash = sk_ASN1_TYPE_value(seq, i);
-       if (hash->type != V_ASN1_OCTET_STRING) {
-               warnx("%s: RSC FileNameAndHash: want ASN.1 OCTET string, have "
-                   "%s (NID %d)", p->fn, ASN1_tag2str(hash->type), hash->type);
-               goto out;
-       }
-
-       if (hash->value.octet_string->length != SHA256_DIGEST_LENGTH) {
-               warnx("%s: RSC Digest: invalid SHA256 length, have %d",
-                   p->fn, hash->value.octet_string->length);
-               goto out;
+       if ((sz = sk_FileNameAndHash_num(checkList)) == 0) {
+               warnx("%s: RSC checkList needs at least one entry", p->fn);
+               return 0;
        }
 
-       p->res->files = recallocarray(p->res->files, p->res->filesz,
-           p->res->filesz + 1, sizeof(struct rscfile));
+       p->res->files = calloc(sz, sizeof(struct rscfile));
        if (p->res->files == NULL)
                err(1, NULL);
+       p->res->filesz = sz;
 
-       rent = &p->res->files[p->res->filesz++];
-       rent->filename = fn;
-       fn = NULL;
-       memcpy(rent->hash, hash->value.octet_string->data,
-           SHA256_DIGEST_LENGTH);
-
-       rc = 1;
- out:
-       free(fn);
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
-}
-
-/*
- * Parse the FileNameAndHash sequence, draft-ietf-sidrops-rpki-rsc
- * section 4.1
- * Return zero on failure, non-zero on success.
- */
-static int
-rsc_parse_checklist(struct parse *p, const ASN1_OCTET_STRING *os)
-{
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *t;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
-       int                      i, rc = 0;
+       for (i = 0; i < sz; i++) {
+               fh = sk_FileNameAndHash_value(checkList, i);
 
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC checkList: failed ASN.1 sequence parse",
-                   p->fn);
-               goto out;
-       }
+               file = &p->res->files[i];
 
-       for (i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
-               t = sk_ASN1_TYPE_value(seq, i);
-               if (t->type != V_ASN1_SEQUENCE) {
-                       warnx("%s: RSC checkList: want ASN.1 sequence, have %s"
-                           " (NID %d)", p->fn, ASN1_tag2str(t->type), t->type);
-                       goto out;
+               if (fh->hash->length != SHA256_DIGEST_LENGTH) {
+                       warnx("%s: RSC Digest: invalid SHA256 length", p->fn);
+                       return 0;
                }
-               if (!rsc_parse_filenamehash(p, t->value.octet_string))
-                       goto out;
-       }
+               memcpy(file->hash, fh->hash->data, SHA256_DIGEST_LENGTH);
 
-       rc = 1;
- out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
-}
-
-/*
- * Convert ASN1 INTEGER and add it to parse results
- * Return zero on failure.
- * XXX: merge with sbgp_asid() in cert.c
- */
-static int
-rsc_parse_asid(struct parse *p, const ASN1_INTEGER *i)
-{
-       struct cert_as           as;
+               if ((fn = fh->fileName) == NULL)
+                       continue;
 
-       memset(&as, 0, sizeof(struct cert_as));
-       as.type = CERT_AS_ID;
+               if (!valid_filename(fn->data, fn->length)) {
+                       warnx("%s: RSC FileNameAndHash: bad filename", p->fn);
+                       return 0;
+               }
 
-       if (!as_id_parse(i, &as.id)) {
-               warnx("%s: RSC malformed AS identifier", p->fn);
-               return 0;
-       }
-       if (as.id == 0) {
-               warnx("%s: RSC AS identifier zero is reserved", p->fn);
-               return 0;
+               file->filename = strndup(fn->data, fn->length);
+               if (file->filename == NULL)
+                       err(1, NULL);
        }
 
-       return append_as(p, &as);
+       return 1;
 }
 
 /*
- * Parse AS Range and add it to parse result
- * Return zero on failure.
- * XXX: merge with sbgp_asrange() in cert.c
+ * parse AsList (inside ResourceBlock)
+ * Return 0 on failure.
  */
 static int
-rsc_parse_asrange(struct parse *p, const unsigned char *d, size_t dsz)
+rsc_parse_aslist(struct parse *p, const ConstrainedASIdentifiers *asids)
 {
-       struct cert_as           as;
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *t;
-       int                      rc = 0;
+       int      i, asz;
 
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: ASRange failed ASN.1 seq parse", p->fn);
-               goto out;
-       }
-
-       if (sk_ASN1_TYPE_num(seq) != 2) {
-               warnx("%s: expected 2 elements in RSC ASRange, have %d",
-                   p->fn, sk_ASN1_TYPE_num(seq));
-               goto out;
-       }
-
-       memset(&as, 0, sizeof(struct cert_as));
-       as.type = CERT_AS_RANGE;
-
-       t = sk_ASN1_TYPE_value(seq, 0);
-       if (t->type != V_ASN1_INTEGER) {
-               warnx("%s: RSC ASRange: want ASN.1 integer, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       if (!as_id_parse(t->value.integer, &as.range.min)) {
-               warnx("%s: RSC malformed AS identifier", p->fn);
-               goto out;
-       }
+       if (asids == NULL)
+               return 1;
 
-       t = sk_ASN1_TYPE_value(seq, 1);
-       if (t->type != V_ASN1_INTEGER) {
-               warnx("%s: RSC ASRange: want ASN.1 integer, have %s (NID %d)",
-                   p->fn, ASN1_tag2str(t->type), t->type);
-               goto out;
-       }
-       if (!as_id_parse(t->value.integer, &as.range.max)) {
-               warnx("%s: RSC malformed AS identifier", p->fn);
-               goto out;
+       if ((asz = sk_ASIdOrRange_num(asids->asnum)) == 0) {
+               warnx("%s: RSC asID empty", p->fn);
+               return 0;
        }
 
-       if (as.range.max == as.range.min) {
-               warnx("%s: RSC ASRange error: range is singular", p->fn);
-               goto out;
-       }
-       if (as.range.max < as.range.min) {
-               warnx("%s: RSC ASRange: range is out of order", p->fn);
-               goto out;
+       if (asz >= MAX_AS_SIZE) {
+               warnx("%s: too many AS number entries: limit %d",
+                   p->fn, MAX_AS_SIZE);
+               return 0;
        }
 
-       if (!append_as(p, &as))
-               goto out;
-
-       rc = 1;
- out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
-}
+       p->res->as = calloc(asz, sizeof(struct cert_as));
+       if (p->res->as == NULL)
+               err(1, NULL);
 
-/*
- * parse AsList (inside ResourceBlock)
- * Return 0 on failure.
- */
-static int
-rsc_parse_aslist(struct parse *p, const unsigned char *d, size_t dsz)
-{
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *t;
-       int                      i, rc = 0;
+       for (i = 0; i < asz; i++) {
+               const ASIdOrRange *aor;
 
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC AsList: failed ASN.1 sequence parse",
-                   p->fn);
-               goto out;
-       }
+               aor = sk_ASIdOrRange_value(asids->asnum, i);
 
-       for (i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
-               t = sk_ASN1_TYPE_value(seq, i);
-               switch (t->type) {
-               case V_ASN1_INTEGER:
-                       if (!rsc_parse_asid(p, t->value.integer))
-                               goto out;
+               switch (aor->type) {
+               case ASIdOrRange_id:
+                       if (!sbgp_as_id(p->fn, p->res->as, &p->res->asz,
+                           aor->u.id))
+                               return 0;
                        break;
-               case V_ASN1_SEQUENCE:
-                       d = t->value.asn1_string->data;
-                       dsz = t->value.asn1_string->length;
-                       if (!rsc_parse_asrange(p, d, dsz))
-                               goto out;
+               case ASIdOrRange_range:
+                       if (!sbgp_as_range(p->fn, p->res->as, &p->res->asz,
+                           aor->u.range))
+                               return 0;
                        break;
                default:
-                       warnx("%s: RSC AsList expected INTEGER or SEQUENCE, "
-                           "have %s (NID %d)", p->fn, ASN1_tag2str(t->type),
-                           t->type);
-                       goto out;
+                       warnx("%s: RSC AsList: unknown type %d", p->fn,
+                           aor->type);
+                       return 0;
                }
        }
 
-       rc = 1;
- out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
+       return 1;
 }
 
-/*
- * parse IPAddressFamilyItem (inside IPList, inside ResourceBlock)
- * Return 0 on failure.
- */
 static int
-rsc_parse_ipaddrfamitem(struct parse *p, const ASN1_OCTET_STRING *os)
+rsc_parse_iplist(struct parse *p, const ConstrainedIPAddrBlocks *ipAddrBlocks)
 {
-       ASN1_OCTET_STRING       *aos = NULL;
-       IPAddressOrRange        *aor = NULL;
-       int                      tag;
-       const unsigned char     *cnt = os->data;
-       long                     cntsz;
-       const unsigned char     *d;
-       struct cert_ip           ip;
-       int                      rc = 0;
-
-       memset(&ip, 0, sizeof(struct cert_ip));
-
-       /*
-        * IPAddressFamilyItem is a sequence containing an addressFamily and
-        * an IPAddressOrRange.
-        */
-       if (!ASN1_frame(p->fn, os->length, &cnt, &cntsz, &tag)) {
-               cryptowarnx("%s: ASN1_frame failed", p->fn);
-               goto out;
-       }
-       if (tag != V_ASN1_SEQUENCE) {
-               warnx("expected ASN.1 sequence, got %d", tag);
-               goto out;
-       }
-
-       d = cnt;
-
-       if ((aos = d2i_ASN1_OCTET_STRING(NULL, &cnt, cntsz)) == NULL) {
-               cryptowarnx("%s: d2i_ASN1_OCTET_STRING failed", p->fn);
-               goto out;
-       }
-
-       cntsz -= cnt - d;
-       assert(cntsz >= 0);
-
-       if (!ip_addr_afi_parse(p->fn, aos, &ip.afi)) {
-               warnx("%s: RSC invalid addressFamily", p->fn);
-               goto out;
-       }
-
-       d = cnt;
-
-       if ((aor = d2i_IPAddressOrRange(NULL, &cnt, cntsz)) == NULL) {
-               warnx("%s: d2i_IPAddressOrRange failed", p->fn);
-               goto out;
-       }
-
-       cntsz -= cnt - d;
-       assert(cntsz >= 0);
-
-       if (cntsz > 0) {
-               warnx("%s: trailing garbage in RSC IPAddressFamilyItem", p->fn);
-               goto out;
-       }
-
-       switch (aor->type) {
-       case IPAddressOrRange_addressPrefix:
-               ip.type = CERT_IP_ADDR;
-               if (!ip_addr_parse(aor->u.addressPrefix, ip.afi, p->fn, &ip.ip))
-                       goto out;
-               break;
-       case IPAddressOrRange_addressRange:
-               ip.type = CERT_IP_RANGE;
-               if (!ip_addr_parse(aor->u.addressRange->min, ip.afi, p->fn,
-                   &ip.range.min))
-                       goto out;
-               if (!ip_addr_parse(aor->u.addressRange->max, ip.afi, p->fn,
-                   &ip.range.max))
-                       goto out;
-               break;
-       default:
-               warnx("%s: unknown addressOrRange type %d\n", p->fn, aor->type);
-               goto out;
-       }
-
-       if (!ip_cert_compose_ranges(&ip)) {
-               warnx("%s: RSC IP address range reversed", p->fn);
-               goto out;
-       }
-
-       if (!append_ip(p, &ip))
-               goto out;
-
-       rc = 1;
- out:
-       ASN1_OCTET_STRING_free(aos);
-       IPAddressOrRange_free(aor);
-       return rc;
-}
+       const ConstrainedIPAddressFamily        *af;
+       const IPAddressOrRanges                 *aors;
+       const IPAddressOrRange                  *aor;
+       size_t                                   ipsz;
+       enum afi                                 afi;
+       int                                      i, j;
 
-static int
-rsc_parse_iplist(struct parse *p, const unsigned char *d, size_t dsz)
-{
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *t;
-       int                      i, rc = 0;
+       if (ipAddrBlocks == NULL)
+               return 1;
 
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC IPList: failed ASN.1 sequence parse",
-                   p->fn);
-               goto out;
+       if (sk_ConstrainedIPAddressFamily_num(ipAddrBlocks) == 0) {
+               warnx("%s: RSC ipAddrBlocks empty", p->fn);
+               return 0;
        }
 
-       for (i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
-               t = sk_ASN1_TYPE_value(seq, i);
-               if (t->type != V_ASN1_SEQUENCE) {
-                       warnx("%s: RSC IPList: want ASN.1 sequence, have %s"
-                           " (NID %d)", p->fn, ASN1_tag2str(t->type), t->type);
-                       goto out;
+       for (i = 0; i < sk_ConstrainedIPAddressFamily_num(ipAddrBlocks); i++) {
+               af = sk_ConstrainedIPAddressFamily_value(ipAddrBlocks, i);
+               aors = af->addressesOrRanges;
+
+               ipsz = p->res->ipsz + sk_IPAddressOrRange_num(aors);
+               if (ipsz >= MAX_IP_SIZE) {
+                       warnx("%s: too many IP address entries: limit %d",
+                           p->fn, MAX_IP_SIZE);
+                       return 0;
                }
-               if (!rsc_parse_ipaddrfamitem(p, t->value.octet_string))
-                       goto out;
-       }
-
-       rc = 1;
- out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
-}
 
-/*
- * Parse a ResourceBlock, draft-ietf-sidrops-rpki-rsc section 4
- * Returns zero on failure, non-zero on success.
- */
-static int
-rsc_parse_resourceblock(const ASN1_OCTET_STRING *os, struct parse *p)
-{
-       ASN1_SEQUENCE_ANY       *seq;
-       const unsigned char     *d = os->data;
-       size_t                   dsz = os->length;
-       int                      i, ptag, rc = 0;
-       const ASN1_TYPE         *t;
-       long                     plen;
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC: ResourceBlock: failed ASN.1 sequence "
-                   "parse", p->fn);
-               goto out;
-       }
-
-       if (sk_ASN1_TYPE_num(seq) == 0) {
-               warnx("%s: ResourceBlock, there must be at least one of asID "
-                   "or ipAddrBlocks", p->fn);
-               goto out;
-       }
+               p->res->ips = recallocarray(p->res->ips, p->res->ipsz, ipsz,
+                   sizeof(struct cert_ip));
+               if (p->res->ips == NULL)
+                       err(1, NULL);
 
-       for (i = 0; i < sk_ASN1_TYPE_num(seq); i++) {
-               t = sk_ASN1_TYPE_value(seq, i);
+               if (!ip_addr_afi_parse(p->fn, af->addressFamily, &afi)) {
+                       warnx("%s: RSC: invalid AFI", p->fn);
+                       return 0;
+               }
 
-               d = t->value.asn1_string->data;
-               dsz = t->value.asn1_string->length;
-               if (!ASN1_frame(p->fn, dsz, &d, &plen, &ptag))
-                       goto out;
-               switch (ptag) {
-               case RSRCBLK_TYPE_ASID:
-                       if (!rsc_parse_aslist(p, d, plen))
-                               goto out;
-                       break;
-               case RSRCBLK_TYPE_IPADDRBLK:
-                       if (!rsc_parse_iplist(p, d, plen))
-                               goto out;
-                       break;
-               default:
-                       warnx("%s: want ASN.1 context specific id, have %s"
-                           " (NID %d)", p->fn, ASN1_tag2str(ptag), ptag);
-                       goto out;
+               for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) {
+                       aor = sk_IPAddressOrRange_value(aors, j);
+                       switch (aor->type) {
+                       case IPAddressOrRange_addressPrefix:
+                               if (!sbgp_addr(p->fn, p->res->ips,
+                                   &p->res->ipsz, afi, aor->u.addressPrefix))
+                                       return 0;
+                               break;
+                       case IPAddressOrRange_addressRange:
+                               if (!sbgp_addr_range(p->fn, p->res->ips,
+                                   &p->res->ipsz, afi, aor->u.addressRange))
+                                       return 0;
+                               break;
+                       default:
+                               warnx("%s: RFC 3779: IPAddressOrRange: "
+                                   "unknown type %d", p->fn, aor->type);
+                               return 0;
+                       }
                }
        }
 
-       rc = 1;
- out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
-       return rc;
+       return 1;
 }
 
 /*
@@ -564,88 +318,61 @@ rsc_parse_resourceblock(const ASN1_OCTET
 static int
 rsc_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p)
 {
-       ASN1_SEQUENCE_ANY       *seq;
-       const ASN1_TYPE         *t;
-       int                      i = 0, rc = 0, sz;
+       RpkiSignedChecklist     *rsc = NULL;
+       ResourceBlock           *resources;
        long                     rsc_version;
+       int                      rc = 0;
 
        /*
         * draft-ietf-sidrops-rpki-rsc section 4
         */
 
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC: RpkiSignedChecklist: failed ASN.1 "
-                   "sequence parse", p->fn);
+       if ((rsc = d2i_RpkiSignedChecklist(NULL, &d, dsz)) == NULL) {
+               cryptowarnx("%s: RSC: failed ASN.1 decode", p->fn);
                goto out;
        }
 
-       if ((sz = sk_ASN1_TYPE_num(seq)) != 3 && sz != 4) {
-               warnx("%s: RSC RpkiSignedChecklist: want 3 or 4 elements, have"
-                   "%d", p->fn, sk_ASN1_TYPE_num(seq));
-               goto out;
-       }
-
-       /*
-        * if there are 4 elements, a version should be present: check it.
-        */
-       if (sz == 4) {
-               t = sk_ASN1_TYPE_value(seq, i++);
-               d = t->value.asn1_string->data;
-               dsz = t->value.asn1_string->length;
-
-               if (cms_econtent_version(p->fn, &d, dsz, &rsc_version) == -1)
+       /* Validate the optional version field */
+       if (rsc->version != NULL) {
+               rsc_version = ASN1_INTEGER_get(rsc->version);
+               if (rsc_version < 0) {
+                       cryptowarnx("%s: RSC: ASN1_INTEGER_get failed", p->fn);
                        goto out;
+               }
 
-               switch (rsc_version) {
+               switch(rsc_version) {
                case 0:
-                       warnx("%s: invalid encoding for version 0", p->fn);
+                       warnx("%s: RSC: incorrect version encoding", p->fn);
                        goto out;
                default:
-                       warnx("%s: version %ld not supported (yet)", p->fn,
+                       warnx("%s: RSC: version %ld not supported (yet)", p->fn,
                            rsc_version);
                        goto out;
                }
        }
 
-       /*
-        * The RSC's eContent ResourceBlock indicates which Internet Number
-        * Resources are associated with the signature over the checkList.
-        */
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_SEQUENCE) {
-               warnx("%s: RSC ResourceBlock: want ASN.1 sequence, have %s"
-                   "(NID %d)", p->fn, ASN1_tag2str(t->type), t->type);
+       resources = rsc->resources;
+       if (resources->asID == NULL && resources->ipAddrBlocks == NULL) {
+               warnx("%s: RSC: one of asID or ipAddrBlocks must be present",
+                   p->fn);
                goto out;
        }
-       if (!rsc_parse_resourceblock(t->value.octet_string, p))
-               goto out;
 
-       /* digestAlgorithm */
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_SEQUENCE) {
-               warnx("%s: RSC DigestAlgorithmIdentifier: want ASN.1 sequence,"
-                   " have %s (NID %d)", p->fn, ASN1_tag2str(t->type), t->type);
+       if (!rsc_parse_aslist(p, resources->asID))
                goto out;
-       }
-       if (!rsc_check_digesttype(p, t->value.asn1_string->data,
-           t->value.asn1_string->length))
+
+       if (!rsc_parse_iplist(p, resources->ipAddrBlocks))
                goto out;
 
-       /*
-        * Now a sequence of FileNameAndHash
-        */
-       t = sk_ASN1_TYPE_value(seq, i++);
-       if (t->type != V_ASN1_SEQUENCE) {
-               warnx("%s: RSC checkList: want ASN.1 sequence, have %s "
-                   "(NID %d)", p->fn, ASN1_tag2str(t->type), t->type);
+       if (!rsc_check_digesttype(p, rsc->digestAlgorithm))
                goto out;
-       }
-       if (!rsc_parse_checklist(p, t->value.octet_string))
+
+       if (!rsc_parse_checklist(p, rsc->checkList))
                goto out;
 
        rc = 1;
  out:
-       sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
+       RpkiSignedChecklist_free(rsc);
        return rc;
 }
 
@@ -657,10 +384,10 @@ struct rsc *
 rsc_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
 {
        struct parse             p;
-       size_t                   cmsz;
        unsigned char           *cms;
-       int                      rc = 0;
+       size_t                   cmsz;
        const ASN1_TIME         *at;
+       int                      rc = 0;
 
        memset(&p, 0, sizeof(struct parse));
        p.fn = fn;

Reply via email to