Rewrite rsc.c using ASN.1 templates
authortb <tb@openbsd.org>
Tue, 31 May 2022 18:40:15 +0000 (18:40 +0000)
committertb <tb@openbsd.org>
Tue, 31 May 2022 18:40:15 +0000 (18:40 +0000)
This implements the constrained versions of the RFC 3779 structures
since OpenSSL's 3779 API doesn't expose IPAddrBlocks. This way we can
also avoid extra checks after walking the structs. Use the previously
exposed sbgp_as_{id,range}() and sbgp_addr{,_range}() to remove a lot
of copy-pasted code.

While parsing ConstrainedASIdentifiers allocate only once and for
ConstrainedIPAddrBlocks allocate once per address family instead of
doing a reallocation for each asid or prefix.

This removes the last explicit use of ASN1_TYPE and ASN1_SEQUENCE_ANY
from rpki-client.

ok claudio job

usr.sbin/rpki-client/rsc.c

index 7e7177a..4f581de 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rsc.c,v 1.4 2022/05/15 16:43:35 tb Exp $ */
+/*     $OpenBSD: rsc.c,v 1.5 2022/05/31 18:40:15 tb Exp $ */
 /*
  * Copyright (c) 2022 Job Snijders <job@fastly.com>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  * 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,612 +41,338 @@ 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;
-
-       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;
-}
+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);
+
+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.4.
  * 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;
-       }
+       FileNameAndHash         *fh;
+       ASN1_IA5STRING          *fn;
+       struct rscfile          *file;
+       size_t                   sz, i;
 
-       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;
-       }
-
-       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;
-}
+       for (i = 0; i < sz; i++) {
+               fh = sk_FileNameAndHash_value(checkList, i);
 
-/*
- * 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;
-
-       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 asID (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;
-
-       if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: ASRange failed ASN.1 seq parse", p->fn);
-               goto out;
-       }
+       int      i, asz;
 
-       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;
-       }
+       if (asids == NULL)
+               return 1;
 
-       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;
-       }
-
-       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;
+       const ConstrainedIPAddressFamily        *af;
+       const IPAddressOrRanges                 *aors;
+       const IPAddressOrRange                  *aor;
+       size_t                                   ipsz;
+       enum afi                                 afi;
+       int                                      i, j;
+
+       if (ipAddrBlocks == NULL)
+               return 1;
+
+       if (sk_ConstrainedIPAddressFamily_num(ipAddrBlocks) == 0) {
+               warnx("%s: RSC ipAddrBlocks empty", p->fn);
+               return 0;
        }
 
-       if (!append_ip(p, &ip))
-               goto out;
+       for (i = 0; i < sk_ConstrainedIPAddressFamily_num(ipAddrBlocks); i++) {
+               af = sk_ConstrainedIPAddressFamily_value(ipAddrBlocks, i);
+               aors = af->addressesOrRanges;
 
-       rc = 1;
- out:
-       ASN1_OCTET_STRING_free(aos);
-       IPAddressOrRange_free(aor);
-       return rc;
-}
-
-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 ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
-               cryptowarnx("%s: RSC IPList: failed ASN.1 sequence parse",
-                   p->fn);
-               goto out;
-       }
-
-       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;
+               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;
 }
 
 /*
- * Parses the eContent segment of a RSC file
+ * Parses the eContent segment of an RSC file
  * draft-ietf-sidrops-rpki-rsc, section 4
  * Returns zero on failure, non-zero on success.
  */
 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;
+       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;