mirror of
https://github.com/openbsd/src.git
synced 2026-04-30 17:16:30 +00:00
Implement Canonical Cache Representation filemode decoder
Decode and verify CCR objects using the profile described in draft-spaghetti-sidrops-rpki-ccr-00 OK & with tb@
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ccr.c,v 1.5 2025/09/06 16:13:48 tb Exp $ */
|
||||
/* $OpenBSD: ccr.c,v 1.6 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2025 Job Snijders <job@openbsd.org>
|
||||
*
|
||||
@@ -231,6 +231,41 @@ ASN1_ITEM_TEMPLATE_END(SubjectKeyIdentifiers);
|
||||
IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(SubjectKeyIdentifiers,
|
||||
SubjectKeyIdentifiers, SubjectKeyIdentifiers);
|
||||
|
||||
static void
|
||||
hash_asn1_item(ASN1_OCTET_STRING *astr, const ASN1_ITEM *it, void *val)
|
||||
{
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
if (!ASN1_item_digest(it, EVP_sha256(), val, hash, NULL))
|
||||
errx(1, "ASN1_item_digest");
|
||||
|
||||
if (!ASN1_OCTET_STRING_set(astr, hash, sizeof(hash)))
|
||||
errx(1, "ASN1_STRING_set");
|
||||
}
|
||||
|
||||
static char *
|
||||
validate_asn1_hash(const char *fn, const char *descr,
|
||||
const ASN1_OCTET_STRING *hash, const ASN1_ITEM *it, void *val)
|
||||
{
|
||||
ASN1_OCTET_STRING *astr = NULL;
|
||||
char *hex = NULL;
|
||||
|
||||
if ((astr = ASN1_OCTET_STRING_new()) == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
hash_asn1_item(astr, it, val);
|
||||
|
||||
if (ASN1_OCTET_STRING_cmp(hash, astr) != 0) {
|
||||
warnx("%s: corrupted %s state", fn, descr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hex = hex_encode(hash->data, hash->length);
|
||||
out:
|
||||
ASN1_OCTET_STRING_free(astr);
|
||||
return hex;
|
||||
}
|
||||
|
||||
static void
|
||||
asn1int_set_seqnum(ASN1_INTEGER *aint, const char *seqnum)
|
||||
{
|
||||
@@ -291,16 +326,10 @@ append_cached_manifest(STACK_OF(ManifestRef) *mftrefs, struct ccr_mft *cm)
|
||||
errx(1, "sk_ManifestRef_push");
|
||||
}
|
||||
|
||||
static void
|
||||
hash_asn1_item(ASN1_OCTET_STRING *astr, const ASN1_ITEM *it, void *val)
|
||||
static int
|
||||
base64_encode_asn1str(const ASN1_OCTET_STRING *astr, char **out)
|
||||
{
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
if (!ASN1_item_digest(it, EVP_sha256(), val, hash, NULL))
|
||||
errx(1, "ASN1_item_digest");
|
||||
|
||||
if (!ASN1_OCTET_STRING_set(astr, hash, sizeof(hash)))
|
||||
errx(1, "ASN1_STRING_set");
|
||||
return base64_encode(astr->data, astr->length, out) == 0;
|
||||
}
|
||||
|
||||
static ManifestState *
|
||||
@@ -327,9 +356,8 @@ generate_manifeststate(struct validation_data *vd)
|
||||
|
||||
hash_asn1_item(ms->hash, ASN1_ITEM_rptr(ManifestRefs), ms->mftrefs);
|
||||
|
||||
if (base64_encode(ms->hash->data, ms->hash->length,
|
||||
&ccr->mfts_hash) == -1)
|
||||
errx(1, "base64_encode");
|
||||
if (!base64_encode_asn1str(ms->hash, &ccr->mfts_hash))
|
||||
errx(1, "base64_encode_asn1str");
|
||||
|
||||
return ms;
|
||||
}
|
||||
@@ -374,6 +402,7 @@ append_cached_vrp(STACK_OF(ROAIPAddress) *addresses, struct vrp *vrp)
|
||||
static ROAPayloadState *
|
||||
generate_roapayloadstate(struct validation_data *vd)
|
||||
{
|
||||
struct ccr *ccr = &vd->ccr;
|
||||
ROAPayloadState *vrps;
|
||||
struct vrp *prev, *vrp;
|
||||
ROAPayloadSet *rp;
|
||||
@@ -384,7 +413,7 @@ generate_roapayloadstate(struct validation_data *vd)
|
||||
errx(1, "ROAPayloadState_new");
|
||||
|
||||
prev = NULL;
|
||||
RB_FOREACH(vrp, ccr_vrp_tree, &vd->ccr.vrps) {
|
||||
RB_FOREACH(vrp, ccr_vrp_tree, &ccr->vrps) {
|
||||
if (prev == NULL || prev->asid != vrp->asid) {
|
||||
if ((rp = ROAPayloadSet_new()) == NULL)
|
||||
errx(1, "ROAPayloadSet_new");
|
||||
@@ -421,9 +450,8 @@ generate_roapayloadstate(struct validation_data *vd)
|
||||
|
||||
hash_asn1_item(vrps->hash, ASN1_ITEM_rptr(ROAPayloadSets), vrps->rps);
|
||||
|
||||
if (base64_encode(vrps->hash->data, vrps->hash->length,
|
||||
&vd->ccr.vrps_hash) == -1)
|
||||
errx(1, "base64_encode");
|
||||
if (!base64_encode_asn1str(vrps->hash, &ccr->vrps_hash))
|
||||
errx(1, "base64_encode_asn1str");
|
||||
|
||||
return vrps;
|
||||
}
|
||||
@@ -460,6 +488,7 @@ append_cached_aspa(STACK_OF(ASPAPayloadSet) *aps, struct vap *vap)
|
||||
static ASPAPayloadState *
|
||||
generate_aspapayloadstate(struct validation_data *vd)
|
||||
{
|
||||
struct ccr *ccr = &vd->ccr;
|
||||
ASPAPayloadState *vaps;
|
||||
struct vap *vap;
|
||||
|
||||
@@ -472,9 +501,8 @@ generate_aspapayloadstate(struct validation_data *vd)
|
||||
|
||||
hash_asn1_item(vaps->hash, ASN1_ITEM_rptr(ASPAPayloadSets), vaps->aps);
|
||||
|
||||
if (base64_encode(vaps->hash->data, vaps->hash->length,
|
||||
&vd->ccr.vaps_hash) == -1)
|
||||
errx(1, "base64_encode");
|
||||
if (!base64_encode_asn1str(vaps->hash, &ccr->vaps_hash))
|
||||
errx(1, "base64_encode_asn1str");
|
||||
|
||||
return vaps;
|
||||
}
|
||||
@@ -723,3 +751,654 @@ output_ccr_der(FILE *out, struct validation_data *vd, struct stats *st)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ccr_mft_free(struct ccr_mft *ccr_mft)
|
||||
{
|
||||
if (ccr_mft == NULL)
|
||||
return;
|
||||
|
||||
free(ccr_mft->seqnum);
|
||||
free(ccr_mft->sia);
|
||||
free(ccr_mft);
|
||||
}
|
||||
|
||||
static void
|
||||
ccr_mfts_free(struct ccr_mft_tree *mfts)
|
||||
{
|
||||
struct ccr_mft *ccr_mft, *tmp_ccr_mft;
|
||||
|
||||
RB_FOREACH_SAFE(ccr_mft, ccr_mft_tree, mfts, tmp_ccr_mft) {
|
||||
RB_REMOVE(ccr_mft_tree, mfts, ccr_mft);
|
||||
ccr_mft_free(ccr_mft);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ccr_vrps_free(struct ccr_vrp_tree *vrps)
|
||||
{
|
||||
struct vrp *vrp, *tmp_vrp;
|
||||
|
||||
RB_FOREACH_SAFE(vrp, ccr_vrp_tree, vrps, tmp_vrp) {
|
||||
RB_REMOVE(ccr_vrp_tree, vrps, vrp);
|
||||
free(vrp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vap_free(struct vap *vap)
|
||||
{
|
||||
if (vap == NULL)
|
||||
return;
|
||||
|
||||
free(vap->providers);
|
||||
free(vap);
|
||||
}
|
||||
|
||||
static void
|
||||
ccr_vaps_free(struct vap_tree *vaps)
|
||||
{
|
||||
struct vap *vap, *tmp_vap;
|
||||
|
||||
RB_FOREACH_SAFE(vap, vap_tree, vaps, tmp_vap) {
|
||||
RB_REMOVE(vap_tree, vaps, vap);
|
||||
vap_free(vap);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ccr_tas_free(struct ccr_tas_tree *tas)
|
||||
{
|
||||
struct ccr_tas_ski *cts, *tmp_cts;
|
||||
|
||||
RB_FOREACH_SAFE(cts, ccr_tas_tree, tas, tmp_cts) {
|
||||
RB_REMOVE(ccr_tas_tree, tas, cts);
|
||||
free(cts);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ccr_free(struct ccr *ccr)
|
||||
{
|
||||
if (ccr == NULL)
|
||||
return;
|
||||
|
||||
ccr_mfts_free(&ccr->mfts);
|
||||
ccr_vrps_free(&ccr->vrps);
|
||||
ccr_vaps_free(&ccr->vaps);
|
||||
ccr_tas_free(&ccr->tas);
|
||||
|
||||
free(ccr->mfts_hash);
|
||||
free(ccr->vrps_hash);
|
||||
free(ccr->vaps_hash);
|
||||
free(ccr->tas_hash);
|
||||
free(ccr->der);
|
||||
free(ccr);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_mft_refs(const char *fn, struct ccr *ccr,
|
||||
const STACK_OF(ManifestRef) *refs)
|
||||
{
|
||||
ManifestRef *ref;
|
||||
struct ccr_mft *ccr_mft = NULL, *prev;
|
||||
int i, refs_num;
|
||||
const ACCESS_DESCRIPTION *ad;
|
||||
int rc = 0;
|
||||
uint64_t size = 0;
|
||||
|
||||
refs_num = sk_ManifestRef_num(refs);
|
||||
|
||||
RB_INIT(&ccr->mfts);
|
||||
|
||||
prev = NULL;
|
||||
for (i = 0; i < refs_num; i++) {
|
||||
if ((ccr_mft = calloc(1, sizeof(*ccr_mft))) == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
ref = sk_ManifestRef_value(refs, i);
|
||||
|
||||
if (ref->hash->length != sizeof(ccr_mft->hash)) {
|
||||
warnx("%s: manifest ref #%d corrupted", fn, i);
|
||||
goto out;
|
||||
}
|
||||
memcpy(ccr_mft->hash, ref->hash->data, ref->hash->length);
|
||||
|
||||
if (prev != NULL) {
|
||||
if (ccr_mft_cmp(ccr_mft, prev) <= 0) {
|
||||
warnx("%s: misordered ManifestRef", fn);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (ref->aki->length != sizeof(ccr_mft->aki)) {
|
||||
warnx("%s: manifest ref #%d corrupted", fn, i);
|
||||
goto out;
|
||||
}
|
||||
memcpy(ccr_mft->aki, ref->aki->data, ref->aki->length);
|
||||
|
||||
if (!ASN1_INTEGER_get_uint64(&size, ref->size)) {
|
||||
warnx("%s: manifest ref #%d corrupted", fn, i);
|
||||
goto out;
|
||||
}
|
||||
if (size < 1000 || size > MAX_FILE_SIZE) {
|
||||
warnx("%s: manifest ref #%d corrupted", fn, i);
|
||||
goto out;
|
||||
}
|
||||
ccr_mft->size = size;
|
||||
|
||||
ccr_mft->seqnum = x509_convert_seqnum(fn, "manifest number",
|
||||
ref->manifestNumber);
|
||||
if (ccr_mft->seqnum == NULL)
|
||||
goto out;
|
||||
|
||||
if (sk_ACCESS_DESCRIPTION_num(ref->location) != 1) {
|
||||
warnx("%s: unexpected number of locations", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ad = sk_ACCESS_DESCRIPTION_value(ref->location, 0);
|
||||
|
||||
if (!x509_location(fn, "SIA: signedObject", ad->location,
|
||||
&ccr_mft->sia))
|
||||
goto out;
|
||||
|
||||
if (RB_INSERT(ccr_mft_tree, &ccr->mfts, ccr_mft) != NULL) {
|
||||
warnx("%s: manifest state corrupted", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
prev = ccr_mft;
|
||||
ccr_mft = NULL;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
ccr_mft_free(ccr_mft);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_manifeststate(const char *fn, struct ccr *ccr, const ManifestState *state)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
ccr->mfts_hash = validate_asn1_hash(fn, "ManifestState", state->hash,
|
||||
ASN1_ITEM_rptr(ManifestRefs), state->mftrefs);
|
||||
if (ccr->mfts_hash == NULL)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* XXX: refactor into a x509_get_generalized_time() function.
|
||||
*/
|
||||
if (ASN1_STRING_length(state->mostRecentUpdate) != GENTIME_LENGTH) {
|
||||
warnx("%s: mostRecentUpdate time format invalid", fn);
|
||||
goto out;
|
||||
}
|
||||
if (!x509_get_time(state->mostRecentUpdate, &ccr->most_recent_update)) {
|
||||
warnx("%s: parsing CCR mostRecentUpdate failed", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!parse_mft_refs(fn, ccr, state->mftrefs))
|
||||
goto out;
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_roa_addresses(const char *fn, struct ccr *ccr, int asid, enum afi afi,
|
||||
const STACK_OF(ROAIPAddress) *addrs)
|
||||
{
|
||||
const ROAIPAddress *r;
|
||||
struct vrp *prev, *vrp = NULL;
|
||||
uint64_t maxlen;
|
||||
int addrs_num, i, rc = 0;
|
||||
|
||||
if ((addrs_num = sk_ROAIPAddress_num(addrs)) <= 0) {
|
||||
warnx("%s: missing ROAIPAddress", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
prev = NULL;
|
||||
for (i = 0; i < addrs_num; i++) {
|
||||
r = sk_ROAIPAddress_value(addrs, i);
|
||||
|
||||
if ((vrp = calloc(1, sizeof(*vrp))) == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
vrp->asid = asid;
|
||||
vrp->afi = afi;
|
||||
|
||||
if (!ip_addr_parse(r->address, afi, fn, &vrp->addr)) {
|
||||
warnx("%s: invalid address in ROAPayload", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
maxlen = vrp->addr.prefixlen;
|
||||
if (r->maxLength != NULL) {
|
||||
if (!ASN1_INTEGER_get_uint64(&maxlen, r->maxLength)) {
|
||||
warnx("%s: ASN1_INTEGER_get_uint64 failed", fn);
|
||||
goto out;
|
||||
}
|
||||
if (vrp->addr.prefixlen > maxlen) {
|
||||
warnx("%s: invalid maxLength", fn);
|
||||
goto out;
|
||||
}
|
||||
if (maxlen > ((afi == AFI_IPV4) ? 32 : 128)) {
|
||||
warnx("%s: maxLength too large", fn);
|
||||
goto out;
|
||||
}
|
||||
vrp->maxlength = maxlen;
|
||||
}
|
||||
|
||||
if (prev != NULL) {
|
||||
if (ccr_vrp_cmp(vrp, prev) <= 0) {
|
||||
warnx("%s: misordered ROAIPAddressFamily", fn);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ((RB_INSERT(ccr_vrp_tree, &ccr->vrps, vrp)) != NULL) {
|
||||
warnx("%s: duplicate ROAIPAddress", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
prev = vrp;
|
||||
vrp = NULL;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
free(vrp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_roa_ipaddrb(const char *fn, struct ccr *ccr, int asid,
|
||||
const STACK_OF(ROAIPAddressFamily) *ipaddrblocks)
|
||||
{
|
||||
const ROAIPAddressFamily *ripaf;
|
||||
enum afi afi;
|
||||
int ipv4_seen = 0, ipv6_seen = 0;
|
||||
int i, rc = 0, ipb_num;
|
||||
|
||||
ipb_num = sk_ROAIPAddressFamily_num(ipaddrblocks);
|
||||
if (ipb_num != 1 && ipb_num != 2) {
|
||||
warnx("%s: unexpected ipAddrBlocks count for AS %d", fn, asid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < ipb_num; i++) {
|
||||
ripaf = sk_ROAIPAddressFamily_value(ipaddrblocks, i);
|
||||
|
||||
if (!ip_addr_afi_parse(fn, ripaf->addressFamily, &afi)) {
|
||||
warnx("%s: invalid afi for AS %d", fn, asid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (afi) {
|
||||
case AFI_IPV4:
|
||||
if (ipv6_seen > 0) {
|
||||
warnx("%s: misordered IPv4 addressFamily for AS"
|
||||
" %d", fn, asid);
|
||||
goto out;
|
||||
}
|
||||
if (ipv4_seen++ > 0) {
|
||||
warnx("%s: IPv4 addressFamily duplicate for AS"
|
||||
" %d", fn, asid);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case AFI_IPV6:
|
||||
if (ipv6_seen++ > 0) {
|
||||
warnx("%s: IPv6 addressFamily duplicate for AS"
|
||||
" %d", fn, asid);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!parse_roa_addresses(fn, ccr, asid, afi, ripaf->addresses))
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_roa_payloads(const char *fn, struct ccr *ccr,
|
||||
const STACK_OF(ROAPayloadSet) *rps)
|
||||
{
|
||||
ROAPayloadSet *rp;
|
||||
int i, rc = 0, rps_num;
|
||||
|
||||
rps_num = sk_ROAPayloadSet_num(rps);
|
||||
|
||||
RB_INIT(&ccr->vrps);
|
||||
|
||||
for (i = 0; i < rps_num; i++) {
|
||||
int asid;
|
||||
|
||||
rp = sk_ROAPayloadSet_value(rps, i);
|
||||
|
||||
if (!as_id_parse(rp->asID, &asid)) {
|
||||
warnx("%s: malformed asID in ROAPayloadSet", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!parse_roa_ipaddrb(fn, ccr, asid, rp->ipAddrBlocks))
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_roastate(const char *fn, struct ccr *ccr, const ROAPayloadState *state)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
ccr->vrps_hash = validate_asn1_hash(fn, "ROAPayloadState", state->hash,
|
||||
ASN1_ITEM_rptr(ROAPayloadSets), state->rps);
|
||||
if (ccr->vrps_hash == NULL)
|
||||
goto out;
|
||||
|
||||
if (!parse_roa_payloads(fn, ccr, state->rps))
|
||||
goto out;
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_aspa_providers(const char *fn, struct ccr *ccr, int asid,
|
||||
STACK_OF(ASN1_INTEGER) *providers)
|
||||
{
|
||||
struct vap *vap = NULL;
|
||||
ASN1_INTEGER *aint;
|
||||
uint32_t prev, provider;
|
||||
int i, p_num, rc = 0;
|
||||
|
||||
if ((p_num = sk_ASN1_INTEGER_num(providers)) <= 0) {
|
||||
warnx("%s: AS %d ASPAPayloadSet providers missing", fn, asid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((vap = calloc(1, sizeof(*vap))) == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
vap->custasid = asid;
|
||||
vap->num_providers = p_num;
|
||||
|
||||
if ((vap->providers = calloc(p_num, sizeof(vap->providers[0]))) == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
for (i = 0; i < p_num; i++) {
|
||||
aint = sk_ASN1_INTEGER_value(providers, i);
|
||||
|
||||
if (!as_id_parse(aint, &provider)) {
|
||||
warnx("%s: AS %d malformed ASPA provider", fn, asid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
if (provider <= prev) {
|
||||
warnx("%s: AS %d misordered providers", fn,
|
||||
asid);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
vap->providers[i] = provider;
|
||||
prev = provider;
|
||||
}
|
||||
|
||||
if ((RB_INSERT(vap_tree, &ccr->vaps, vap)) != NULL) {
|
||||
warnx("%s: duplicate ASPAPayloadSet", fn);
|
||||
goto out;
|
||||
}
|
||||
vap = NULL;
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
vap_free(vap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_aspa_payloads(const char *fn, struct ccr *ccr,
|
||||
const STACK_OF(ASPAPayloadSet) *aps)
|
||||
{
|
||||
ASPAPayloadSet *a;
|
||||
uint32_t asid, prev;
|
||||
int i, rc = 0, aps_num;
|
||||
|
||||
aps_num = sk_ASPAPayloadSet_num(aps);
|
||||
|
||||
RB_INIT(&ccr->vaps);
|
||||
|
||||
for (i = 0; i < aps_num; i++) {
|
||||
a = sk_ASPAPayloadSet_value(aps, i);
|
||||
|
||||
if (!as_id_parse(a->asID, &asid)) {
|
||||
warnx("%s: malformed asID in ASPAPayloadSet", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
if (asid <= prev) {
|
||||
warnx("%s: ASPAPayloadState misordered", fn);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parse_aspa_providers(fn, ccr, asid, a->providers))
|
||||
goto out;
|
||||
|
||||
prev = asid;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_aspastate(const char *fn, struct ccr *ccr, const ASPAPayloadState *state)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
ccr->vaps_hash = validate_asn1_hash(fn, "ASPAPayloadState", state->hash,
|
||||
ASN1_ITEM_rptr(ASPAPayloadSets), state->aps);
|
||||
if (ccr->vaps_hash == NULL)
|
||||
goto out;
|
||||
|
||||
if (!parse_aspa_payloads(fn, ccr, state->aps))
|
||||
goto out;
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_tas_skis(const char *fn, struct ccr *ccr,
|
||||
const STACK_OF(SubjectKeyIdentifier) *skis)
|
||||
{
|
||||
SubjectKeyIdentifier *ski;
|
||||
struct ccr_tas_ski *cts = NULL, *prev;
|
||||
int i, rc = 0, skis_num;
|
||||
|
||||
if ((skis_num = sk_SubjectKeyIdentifier_num(skis)) <= 0) {
|
||||
warnx("%s: missing TrustAnchorState SubjectKeyIdentifier", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
RB_INIT(&ccr->tas);
|
||||
|
||||
prev = NULL;
|
||||
for (i = 0; i < skis_num; i++) {
|
||||
if ((cts = calloc(1, sizeof(*cts))) == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
ski = sk_SubjectKeyIdentifier_value(skis, i);
|
||||
|
||||
if (ski->length != sizeof(cts->keyid)) {
|
||||
warnx("%s: TAS SKI #%d corrupted", fn, i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(cts->keyid, ski->data, ski->length);
|
||||
|
||||
if (prev != NULL) {
|
||||
if (ccr_tas_ski_cmp(cts, prev) <= 0) {
|
||||
warnx("%s: misordered TrustAnchorState", fn);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (RB_INSERT(ccr_tas_tree, &ccr->tas, cts) != NULL) {
|
||||
warnx("%s: trust anchor state corrupted", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
prev = cts;
|
||||
cts = NULL;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
free(cts);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_tastate(const char *fn, struct ccr *ccr, const TrustAnchorState *state)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
ccr->tas_hash = validate_asn1_hash(fn, "TrustAnchorState", state->hash,
|
||||
ASN1_ITEM_rptr(SubjectKeyIdentifiers), state->skis);
|
||||
if (ccr->tas_hash == NULL)
|
||||
goto out;
|
||||
|
||||
if (!parse_tas_skis(fn, ccr, state->skis))
|
||||
goto out;
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct ccr *
|
||||
ccr_parse(const char *fn, const unsigned char *der, size_t len)
|
||||
{
|
||||
const unsigned char *oder;
|
||||
ContentInfo *ci = NULL;
|
||||
CanonicalCacheRepresentation *ccr_asn1 = NULL;
|
||||
struct ccr *ccr = NULL;
|
||||
int nid, rc = 0;
|
||||
|
||||
if (der == NULL)
|
||||
return NULL;
|
||||
|
||||
oder = der;
|
||||
if ((ci = d2i_ContentInfo(NULL, &der, len)) == NULL) {
|
||||
warnx("%s: d2i_ContentInfo", fn);
|
||||
goto out;
|
||||
}
|
||||
if (der != oder + len) {
|
||||
warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (OBJ_cmp(ci->contentType, ccr_oid) != 0) {
|
||||
char buf[128];
|
||||
|
||||
OBJ_obj2txt(buf, sizeof(buf), ci->contentType, 1);
|
||||
warnx("%s: unexpected OID: got %s, want 1.3.6.1.4.1.41948.825",
|
||||
fn, buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
der = ASN1_STRING_get0_data(ci->content);
|
||||
len = ASN1_STRING_length(ci->content);
|
||||
|
||||
oder = der;
|
||||
ccr_asn1 = d2i_CanonicalCacheRepresentation(NULL, &der, len);
|
||||
if (ccr_asn1 == NULL) {
|
||||
warnx("%s: d2i_CanonicalCacheRepresentation failed", fn);
|
||||
goto out;
|
||||
}
|
||||
if (der != oder + len) {
|
||||
warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!valid_econtent_version(fn, ccr_asn1->version, 0))
|
||||
goto out;
|
||||
|
||||
if ((nid = OBJ_obj2nid(ccr_asn1->hashAlg)) != NID_sha256) {
|
||||
warnx("%s: hashAlg: want SHA256 object, have %s", fn,
|
||||
nid2str(nid));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((ccr = calloc(1, sizeof(*ccr))) == NULL)
|
||||
err(1, NULL);
|
||||
|
||||
if (ASN1_STRING_length(ccr_asn1->producedAt) != GENTIME_LENGTH) {
|
||||
warnx("%s: embedded from time format invalid", fn);
|
||||
goto out;
|
||||
}
|
||||
if (!x509_get_time(ccr_asn1->producedAt, &ccr->producedat)) {
|
||||
warnx("%s: parsing CCR producedAt failed", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ccr_asn1->mfts == NULL && ccr_asn1->vrps == NULL &&
|
||||
ccr_asn1->vaps == NULL && ccr_asn1->tas == NULL) {
|
||||
warnx("%s: must contain at least one state component", fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ccr_asn1->mfts != NULL) {
|
||||
if (!parse_manifeststate(fn, ccr, ccr_asn1->mfts))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ccr_asn1->vrps != NULL) {
|
||||
if (!parse_roastate(fn, ccr, ccr_asn1->vrps))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ccr_asn1->vaps != NULL) {
|
||||
if (!parse_aspastate(fn, ccr, ccr_asn1->vaps))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ccr_asn1->tas != NULL) {
|
||||
if (!parse_tastate(fn, ccr, ccr_asn1->tas))
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
out:
|
||||
CanonicalCacheRepresentation_free(ccr_asn1);
|
||||
ContentInfo_free(ci);
|
||||
|
||||
if (rc == 0) {
|
||||
ccr_free(ccr);
|
||||
ccr = NULL;
|
||||
}
|
||||
|
||||
return ccr;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: encoding.c,v 1.13 2022/05/15 15:00:53 deraadt Exp $ */
|
||||
/* $OpenBSD: encoding.c,v 1.14 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
|
||||
*
|
||||
@@ -47,7 +47,7 @@ load_file(const char *name, size_t *len)
|
||||
return NULL;
|
||||
if (fstat(fd, &st) != 0)
|
||||
goto err;
|
||||
if (st.st_size <= 0 || st.st_size > MAX_FILE_SIZE) {
|
||||
if (st.st_size <= 0 || (!filemode && st.st_size > MAX_FILE_SIZE)) {
|
||||
errno = EFBIG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: extern.h,v 1.261 2025/09/06 11:55:44 job Exp $ */
|
||||
/* $OpenBSD: extern.h,v 1.262 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
@@ -202,6 +202,7 @@ enum rtype {
|
||||
RTYPE_TAK,
|
||||
RTYPE_GEOFEED,
|
||||
RTYPE_SPL,
|
||||
RTYPE_CCR,
|
||||
};
|
||||
|
||||
enum location {
|
||||
@@ -487,11 +488,14 @@ RB_PROTOTYPE(ccr_tas_tree, ccr_tas_ski, entry, ccr_tas_ski_cmp);
|
||||
struct ccr {
|
||||
struct ccr_mft_tree mfts;
|
||||
struct ccr_vrp_tree vrps;
|
||||
struct vap_tree vaps; /* only used in filemode */
|
||||
struct ccr_tas_tree tas;
|
||||
char *mfts_hash;
|
||||
char *vrps_hash;
|
||||
char *vaps_hash;
|
||||
char *tas_hash;
|
||||
time_t producedat;
|
||||
time_t most_recent_update;
|
||||
unsigned char *der;
|
||||
size_t der_len;
|
||||
};
|
||||
@@ -707,6 +711,7 @@ extern ASN1_OBJECT *aspa_oid;
|
||||
extern ASN1_OBJECT *tak_oid;
|
||||
extern ASN1_OBJECT *geofeed_oid;
|
||||
extern ASN1_OBJECT *spl_oid;
|
||||
extern ASN1_OBJECT *ccr_oid;
|
||||
|
||||
extern int verbose;
|
||||
extern int noop;
|
||||
@@ -1026,6 +1031,9 @@ int output_ccr_der(FILE *, struct validation_data *, struct stats *);
|
||||
/*
|
||||
* Canonical Cache Representation
|
||||
*/
|
||||
void ccr_free(struct ccr *);
|
||||
void ccr_print(struct ccr *);
|
||||
struct ccr *ccr_parse(const char *, const unsigned char *, size_t);
|
||||
void ccr_insert_mft(struct ccr_mft_tree *, const struct mft *);
|
||||
void ccr_insert_roa(struct ccr_vrp_tree *, const struct roa *);
|
||||
void ccr_insert_tas(struct ccr_tas_tree *, const struct cert *);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: filemode.c,v 1.67 2025/08/01 16:33:58 tb Exp $ */
|
||||
/* $OpenBSD: filemode.c,v 1.68 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
@@ -413,6 +413,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
|
||||
static int num;
|
||||
struct aspa *aspa = NULL;
|
||||
struct cert *cert = NULL;
|
||||
struct ccr *ccr = NULL;
|
||||
struct crl *crl = NULL;
|
||||
struct gbr *gbr = NULL;
|
||||
struct geofeed *geofeed = NULL;
|
||||
@@ -482,6 +483,12 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
|
||||
notbefore = &cert->notbefore;
|
||||
notafter = &cert->notafter;
|
||||
break;
|
||||
case RTYPE_CCR:
|
||||
ccr = ccr_parse(file, buf, len);
|
||||
if (ccr == NULL)
|
||||
break;
|
||||
ccr_print(ccr);
|
||||
break;
|
||||
case RTYPE_CER:
|
||||
cert = cert_parse(file, buf, len);
|
||||
if (cert == NULL)
|
||||
@@ -717,6 +724,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
|
||||
out:
|
||||
aspa_free(aspa);
|
||||
cert_free(cert);
|
||||
ccr_free(ccr);
|
||||
crl_free(crl);
|
||||
gbr_free(gbr);
|
||||
geofeed_free(geofeed);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: main.c,v 1.297 2025/09/06 11:55:44 job Exp $ */
|
||||
/* $OpenBSD: main.c,v 1.298 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
@@ -1016,7 +1016,7 @@ main(int argc, char *argv[])
|
||||
const char *skiplistfile = NULL;
|
||||
struct rusage ru;
|
||||
struct timespec start_time, now_time;
|
||||
struct validation_data vd;
|
||||
struct validation_data vd = { 0 };
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: mft.c,v 1.130 2025/08/24 12:17:12 tb Exp $ */
|
||||
/* $OpenBSD: mft.c,v 1.131 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
|
||||
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
@@ -58,8 +58,6 @@ ASN1_SEQUENCE(FileAndHash) = {
|
||||
ASN1_SIMPLE(FileAndHash, hash, ASN1_BIT_STRING),
|
||||
} ASN1_SEQUENCE_END(FileAndHash);
|
||||
|
||||
#define GENTIME_LENGTH 15
|
||||
|
||||
/*
|
||||
* Determine rtype corresponding to file extension. Returns RTYPE_INVALID
|
||||
* on error or unknown extension.
|
||||
@@ -95,6 +93,8 @@ rtype_from_file_extension(const char *fn)
|
||||
return RTYPE_GEOFEED;
|
||||
if (strcasecmp(fn + sz - 4, ".spl") == 0)
|
||||
return RTYPE_SPL;
|
||||
if (strcasecmp(fn + sz - 4, ".ccr") == 0)
|
||||
return RTYPE_CCR;
|
||||
|
||||
return RTYPE_INVALID;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: print.c,v 1.65 2025/07/20 14:23:44 tb Exp $ */
|
||||
/* $OpenBSD: print.c,v 1.66 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
@@ -883,3 +883,216 @@ geofeed_print(const struct cert *c, const struct geofeed *p)
|
||||
if (outformats & FORMAT_JSON)
|
||||
json_do_end();
|
||||
}
|
||||
|
||||
static void
|
||||
print_ccr_mftstate(struct ccr *ccr)
|
||||
{
|
||||
char *aki, *hash;
|
||||
struct ccr_mft *ccr_mft;
|
||||
|
||||
if (base64_encode(ccr->mfts_hash, SHA256_DIGEST_LENGTH, &hash) == -1)
|
||||
errx(1, "base64_encode");
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_object("manifest_state", 0);
|
||||
json_do_int("most_recent_update", ccr->most_recent_update);
|
||||
json_do_string("hash", hash);
|
||||
json_do_array("refs");
|
||||
} else {
|
||||
printf("Manifest state hash: %s\n", hash);
|
||||
printf("Manifest last update: %s\n",
|
||||
time2str(ccr->most_recent_update));
|
||||
printf("Manifest references: \n");
|
||||
}
|
||||
free(hash);
|
||||
|
||||
RB_FOREACH(ccr_mft, ccr_mft_tree, &ccr->mfts) {
|
||||
if (base64_encode(ccr_mft->hash, SHA256_DIGEST_LENGTH, &hash)
|
||||
== -1)
|
||||
errx(1, "base64_encode");
|
||||
aki = hex_encode(ccr_mft->aki, SHA_DIGEST_LENGTH);
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_object("ref", 1);
|
||||
json_do_string("hash", hash);
|
||||
json_do_uint("size", ccr_mft->size);
|
||||
json_do_string("aki", aki);
|
||||
json_do_string("seqnum", ccr_mft->seqnum);
|
||||
json_do_string("sia", ccr_mft->sia);
|
||||
json_do_end();
|
||||
} else {
|
||||
printf("%26shash:%s size:%zu aki:%s seqnum:%s sia:%s\n",
|
||||
"", hash, ccr_mft->size, aki, ccr_mft->seqnum,
|
||||
ccr_mft->sia);
|
||||
}
|
||||
|
||||
free(aki);
|
||||
free(hash);
|
||||
}
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_end();
|
||||
json_do_end();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_ccr_roastate(struct ccr *ccr)
|
||||
{
|
||||
char buf[64], *hash;
|
||||
struct vrp *vrp;
|
||||
|
||||
if (base64_encode(ccr->vrps_hash, SHA256_DIGEST_LENGTH, &hash) == -1)
|
||||
errx(1, "base64_encode");
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_object("roapayload_state", 0);
|
||||
json_do_string("hash", hash);
|
||||
json_do_array("vrps");
|
||||
} else {
|
||||
printf("ROA payload state hash: %s\n", hash);
|
||||
printf("ROA payload entries:\n");
|
||||
}
|
||||
free(hash);
|
||||
|
||||
RB_FOREACH(vrp, ccr_vrp_tree, &ccr->vrps) {
|
||||
ip_addr_print(&vrp->addr, vrp->afi, buf, sizeof(buf));
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_object("vrp", 1);
|
||||
json_do_string("prefix", buf);
|
||||
json_do_int("asn", vrp->asid);
|
||||
if (vrp->maxlength)
|
||||
json_do_int("maxlen", vrp->maxlength);
|
||||
json_do_end();
|
||||
} else {
|
||||
printf("%26s%s", "", buf);
|
||||
if (vrp->maxlength)
|
||||
printf("-%hhu", vrp->maxlength);
|
||||
printf(" AS %u\n", vrp->asid);
|
||||
}
|
||||
|
||||
}
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_end();
|
||||
json_do_end();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_ccr_aspastate(struct ccr *ccr)
|
||||
{
|
||||
char *hash;
|
||||
struct vap *vap;
|
||||
size_t i;
|
||||
|
||||
if (base64_encode(ccr->vaps_hash, SHA256_DIGEST_LENGTH, &hash) == -1)
|
||||
errx(1, "base64_encode");
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_object("aspapayload_state", 0);
|
||||
json_do_string("hash", hash);
|
||||
json_do_array("vaps");
|
||||
} else {
|
||||
printf("ASPA payload state hash: %s\n", hash);
|
||||
printf("ASPA payload entries:\n");
|
||||
}
|
||||
free(hash);
|
||||
|
||||
RB_FOREACH(vap, vap_tree, &ccr->vaps) {
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_object("vap", 1);
|
||||
json_do_uint("customer_asid", vap->custasid);
|
||||
json_do_array("providers");
|
||||
} else {
|
||||
printf("%26s", "");
|
||||
printf("customer: %d providers: ", vap->custasid);
|
||||
}
|
||||
|
||||
for (i = 0; i < vap->num_providers; i++) {
|
||||
if (outformats & FORMAT_JSON)
|
||||
json_do_uint("provider", vap->providers[i]);
|
||||
else {
|
||||
if (i > 0)
|
||||
printf(", ");
|
||||
printf("%u", vap->providers[i]);
|
||||
}
|
||||
}
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_end();
|
||||
json_do_end();
|
||||
} else
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_end();
|
||||
json_do_end();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_ccr_tastate(struct ccr *ccr)
|
||||
{
|
||||
char *hash, *ski;
|
||||
struct ccr_tas_ski *cts;
|
||||
int i = 0;
|
||||
|
||||
if (base64_encode(ccr->tas_hash, SHA256_DIGEST_LENGTH, &hash) == -1)
|
||||
errx(1, "base64_encode");
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_object("trustanchor_state", 0);
|
||||
json_do_string("hash", hash);
|
||||
json_do_array("skis");
|
||||
} else {
|
||||
printf("Trust anchor state hash: %s\n", hash);
|
||||
printf("Trust anchor keyids: ");
|
||||
}
|
||||
|
||||
free(hash);
|
||||
|
||||
RB_FOREACH(cts, ccr_tas_tree, &ccr->tas) {
|
||||
ski = hex_encode(cts->keyid, sizeof(cts->keyid));
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_string("ski", ski);
|
||||
} else {
|
||||
if (++i > 1)
|
||||
printf(", ");
|
||||
printf("%s", ski);
|
||||
}
|
||||
|
||||
free(ski);
|
||||
}
|
||||
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_end();
|
||||
json_do_end();
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ccr_print(struct ccr *ccr)
|
||||
{
|
||||
if (outformats & FORMAT_JSON) {
|
||||
json_do_string("type", "ccr");
|
||||
json_do_int("produced_at", ccr->producedat);
|
||||
} else {
|
||||
printf("CCR produced at: %s\n",
|
||||
time2str(ccr->producedat));
|
||||
}
|
||||
|
||||
if (ccr->mfts_hash != NULL)
|
||||
print_ccr_mftstate(ccr);
|
||||
|
||||
if (ccr->vrps_hash != NULL)
|
||||
print_ccr_roastate(ccr);
|
||||
|
||||
if (ccr->vaps_hash != NULL)
|
||||
print_ccr_aspastate(ccr);
|
||||
|
||||
if (ccr->tas_hash != NULL)
|
||||
print_ccr_tastate(ccr);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: roa.c,v 1.86 2025/08/24 11:52:20 tb Exp $ */
|
||||
/* $OpenBSD: roa.c,v 1.87 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
|
||||
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
@@ -95,6 +95,11 @@ roa_parse_econtent(const char *fn, struct roa *roa, const unsigned char *d,
|
||||
if (!valid_econtent_version(fn, roa_asn1->version, 0))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* XXX: from here until the function end should be refactored
|
||||
* to deduplicate similar code in ccr.c.
|
||||
*/
|
||||
|
||||
if (!as_id_parse(roa_asn1->asid, &roa->asid)) {
|
||||
warnx("%s: RFC 9582 section 4.2: asID: "
|
||||
"malformed AS identifier", fn);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: rpki-asn1.h,v 1.3 2025/09/06 11:55:44 job Exp $ */
|
||||
/* $OpenBSD: rpki-asn1.h,v 1.4 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2025 Job Snijders <job@openbsd.org>
|
||||
* Copyright (c) 2025 Theo Buehler <tb@openbsd.org>
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/asn1t.h>
|
||||
|
||||
#define GENTIME_LENGTH 15
|
||||
|
||||
/*
|
||||
* Autonomous System Provider Authorization (ASPA)
|
||||
* reference: draft-ietf-sidrops-aspa-profile
|
||||
@@ -69,6 +71,7 @@ DECLARE_STACK_OF(ManifestRef);
|
||||
#ifndef DEFINE_STACK_OF
|
||||
#define sk_ManifestRef_num(st) SKM_sk_num(ManifestRef, (st))
|
||||
#define sk_ManifestRef_push(st, i) SKM_sk_push(ManifestRef, (st), (i))
|
||||
#define sk_ManifestRef_value(st, i) SKM_sk_value(ManifestRef, (st), (i))
|
||||
#endif
|
||||
|
||||
DECLARE_ASN1_FUNCTIONS(ManifestRef);
|
||||
@@ -95,6 +98,7 @@ DECLARE_STACK_OF(ROAPayloadSet);
|
||||
#ifndef DEFINE_STACK_OF
|
||||
#define sk_ROAPayloadSet_num(st) SKM_sk_num(ROAPayloadSet, (st))
|
||||
#define sk_ROAPayloadSet_push(st, i) SKM_sk_push(ROAPayloadSet, (st), (i))
|
||||
#define sk_ROAPayloadSet_value(st, i) SKM_sk_value(ROAPayloadSet, (st), (i))
|
||||
#endif
|
||||
|
||||
DECLARE_ASN1_FUNCTIONS(ROAPayloadSet);
|
||||
@@ -120,6 +124,7 @@ DECLARE_STACK_OF(ASPAPayloadSet);
|
||||
#ifndef DEFINE_STACK_OF
|
||||
#define sk_ASPAPayloadSet_num(st) SKM_sk_num(ASPAPayloadSet, (st))
|
||||
#define sk_ASPAPayloadSet_push(st, i) SKM_sk_push(ASPAPayloadSet, (st), (i))
|
||||
#define sk_ASPAPayloadSet_value(st, i) SKM_sk_value(ASPAPayloadSet, (st), (i))
|
||||
#endif
|
||||
|
||||
DECLARE_ASN1_FUNCTIONS(ASPAPayloadSet);
|
||||
@@ -142,8 +147,11 @@ DECLARE_ASN1_FUNCTIONS(SubjectKeyIdentifier);
|
||||
DECLARE_STACK_OF(SubjectKeyIdentifier);
|
||||
|
||||
#ifndef DEFINE_STACK_OF
|
||||
#define sk_SubjectKeyIdentifier_num(st) SKM_sk_num(SubjectKeyIdentifier, (st))
|
||||
#define sk_SubjectKeyIdentifier_push(st, i) \
|
||||
SKM_sk_push(SubjectKeyIdentifier, (st), (i))
|
||||
#define sk_SubjectKeyIdentifier_value(st, i) \
|
||||
SKM_sk_value(SubjectKeyIdentifier, (st), (i))
|
||||
#endif
|
||||
|
||||
typedef STACK_OF(SubjectKeyIdentifier) SubjectKeyIdentifiers;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: x509.c,v 1.117 2025/08/01 17:29:30 tb Exp $ */
|
||||
/* $OpenBSD: x509.c,v 1.118 2025/09/09 08:23:24 job Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
|
||||
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
|
||||
@@ -46,6 +46,7 @@ ASN1_OBJECT *aspa_oid; /* id-ct-ASPA */
|
||||
ASN1_OBJECT *tak_oid; /* id-ct-SignedTAL */
|
||||
ASN1_OBJECT *geofeed_oid; /* id-ct-geofeedCSVwithCRLF */
|
||||
ASN1_OBJECT *spl_oid; /* id-ct-signedPrefixList */
|
||||
ASN1_OBJECT *ccr_oid; /* CanonicalCacheRepresentation PEN OID */
|
||||
|
||||
static const struct {
|
||||
const char *oid;
|
||||
@@ -123,6 +124,10 @@ static const struct {
|
||||
.oid = "1.2.840.113549.1.9.16.1.51",
|
||||
.ptr = &spl_oid,
|
||||
},
|
||||
{
|
||||
.oid = "1.3.6.1.4.1.41948.825",
|
||||
.ptr = &ccr_oid,
|
||||
},
|
||||
};
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user