Index | Thread | Search

From:
Job Snijders <job@openbsd.org>
Subject:
rpki-client: roadmap towards shorter-lived Trust Anchor certificates
To:
tech@openbsd.org
Date:
Mon, 16 Dec 2024 22:06:05 +0000

Download raw body.

Thread
Dear all,

The RPKI ecosystem suffers from a partially unmitigated risk related to
long-lived Trust Anchor certificate issuances.

For this to make sense, don't confuse certificates and keypairs! TA
keypairs (and TALs) are expected to be very long-lived and are to be
used periodically to issue new instances of (shorter-lived) TA
certificates.

Issues could arise when a on-path attackers (or operational errors such
as restoring a webserver's data from too old an backup) bring back into
circulation old (but still valid) TA certificate. Older certificates
remain valid for the duration of their validity period, because TA
certificates - being top of the chain - cannot be revoked.

Here I shared real world examples of potential replayable certificates.
Old problematic TA certificates that - today - still would pass validation:
https://mailarchive.ietf.org/arch/msg/sidrops/NxzvSFH0sPXEmyfOS99cLApFKqM/

The trouble with these replayable TA certificates is that when an
on-path entity ends up presenting such an outdated-but-still-valid
certificate to the RP, it's acceptance would damage the RP's local
validated cache. Parts of the validated output will disappear, in an
unpredictably manner.

Periodic reissuance is important because TA certificates are not
entirely static, which of course is why replay might even be an issue in
the first place!. There are 3 'dynamic' fields in TA certificates:

  - the validity period (notBefore, notAfter)
  - the SubjectInfoAccess (where can the RP find the first repository?)
  - the extensions for IP addresses & AS identifiers (RFC 3779 INRs)
    (the RFC 3779 extensions are of critical importance to the
    RPKI's chain validation algorithm)

RIRs will want RPs to validate using the 'latest' issuance of the TA
certificate, because a TA cert from 10 years ago obviously will be 10
years behind on operational decisions, potential SIA migrations,
resource transfers, new IANA assignments, or any other updates to the
RIR's current holdings.

So how do we make good on this situation again? (knowing the 100 year
certs can't be revoked) Preferably without all RIRs having to rekey &
redistribute new TALs?

The plan to overcome this risk has three steps:

step 1) RPs to prefer shorter-lived Trust Anchor certificates over
        longer-lived ones. (rpki-client already implemented this)
        https://datatracker.ietf.org/doc/html/draft-ietf-sidrops-rpki-ta-tiebreaker

step 2) RPs ship with scheduled future refusal of ultra long-lived Trust
        Anchor certificates (that's the below diff).

step 3) Consequently, RIRs have to reissue shorter-lived TA certificates
        to avoid being rejected by RPs.

The end result is that after anno 2026 / 2027, if 100 year or 10 year
certs somehow be brought back into circulation, RPs will simply refuse
such long-lived certs, despite them technically being 'valid'.

Why this works:

The ta-tiebreaker mechanism provides an incentive for TA operators to
reissue with reasonable (1 or 2 year) validity periods, as those certs
will be preferred. In turn, RPs scheduling refusal of long-lived certs
at a predetermined future point in time, relieves TA operators from
worrying about previously issued certs with ultra long lifetimes. It is
a win win for everyone in the ecosystem.

Scheduling details:

I picked February 2nd 2026 for phase 1, because 02-02-2026 and
02-02-2026 are unambiguous dates in both the US and elsewhere. I picked
March 3rd 2027 for phase 2, because 03-03-2027 also is unambiguous and
visually is very distinct from 02-02-2026.

My hope is that a schedule like this will make global coordination less
error-prone, I also wouldn't want to impose undue burden forcing TA
operators to reissue at short notice without adequate preparation time.

The below was joint work with tb@

Thoughts? OK?

Kind regards,

Job

Index: cert.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v
diff -u -p -r1.153 cert.c
--- cert.c	12 Nov 2024 09:23:07 -0000	1.153
+++ cert.c	16 Dec 2024 21:02:15 -0000
@@ -1061,6 +1061,72 @@ badcert:
 	return NULL;
 }
 
+/*
+ * Reject TA certificates with an overly long validity period.
+ *
+ * The schedule is as follows:
+ * Before February 2nd, 2026, warn on TA certs valid for longer than 15 years.
+ * After February 2nd, 2026, reject TA certs valid for longer than 15 years.
+ * Before March 3rd, 2027, warn on TA certs valid for longer than 3 years.
+ * After March 3rd, 2027, reject TA certs valid for longer than 3 years.
+ *
+ * Return 1 if the validity period is acceptable and 0 otherwise.
+ */
+static int
+ta_check_validity(const char *fn, const struct cert *p, time_t now)
+{
+	time_t validity = p->notafter - p->notbefore;
+	time_t cutoff_15y = 1769990400; /* 2026-02-02T00:00:00Z */
+	time_t cutoff_3y = 1804032000; /* 2027-03-03T00:00:00Z */
+	time_t cutoff = cutoff_3y;
+	int warn_years = 3;
+	int exceeds_15y = 0, exceeds_3y = 0;
+	int complain = 0, acceptable = 1;
+
+	if (validity >= 15 * 365 * 86400)
+		exceeds_15y = 1;
+	if (validity >= 3 * 365 * 86400)
+		exceeds_3y = 1;
+
+	if (now < cutoff_15y) {
+		warn_years = 15;
+		cutoff = cutoff_15y;
+		if (exceeds_15y)
+			complain = 1;
+	} else if (now < cutoff_3y) {
+		if (exceeds_15y)
+			acceptable = 0;
+		if (exceeds_3y)
+			complain = 1;
+	} else if (exceeds_3y) {
+		acceptable = 0;
+		complain = 1;
+	}
+
+	/*
+	 * Suppress warnings for previously fetched TAs certs.
+	 */
+	if (!verbose && strncmp(fn, "ta/", strlen("ta/")) == 0)
+		goto out;
+
+	if (!acceptable) {
+		warnx("%s: TA cert rejected: validity period exceeds %d years. "
+		    "Ask the TA operator to reissue their TA cert with a "
+		    "shorter validity period.", fn, warn_years);
+		goto out;
+	}
+
+	if (complain) {
+		warnx("%s: TA validity period exceeds %d years. After %s this "
+		    "certificate will be rejected.", fn, warn_years,
+		    time2str(cutoff));
+		goto out;
+	}
+
+ out:
+	return acceptable;
+}
+
 struct cert *
 ta_parse(const char *fn, struct cert *p, const unsigned char *pkey,
     size_t pkeysz)
@@ -1086,6 +1152,7 @@ ta_parse(const char *fn, struct cert *p,
 		    "pubkey does not match TAL pubkey", fn);
 		goto badcert;
 	}
+
 	if (p->notbefore > now) {
 		warnx("%s: certificate not yet valid", fn);
 		goto badcert;
@@ -1094,6 +1161,9 @@ ta_parse(const char *fn, struct cert *p,
 		warnx("%s: certificate has expired", fn);
 		goto badcert;
 	}
+	if (!ta_check_validity(fn, p, now))
+		goto badcert;
+
 	if (p->aki != NULL && strcmp(p->aki, p->ski)) {
 		warnx("%s: RFC 6487 section 4.8.3: "
 		    "trust anchor AKI, if specified, must match SKI", fn);