Download raw body.
rpki-client: openssl 4 compat
This adds two compat implementations for the OpenSSL 4 API that is
required to deal with opaque strings. This API will eventually land in
libcrypto but I did not manage to do that in this cycle for various
reasons.
Once LibreSSL provides this, the file will move to portable, but for
now this way is simplest to ensure that rpki-client as expected with all
libcrypto versions we care about.
I did not guard the prototypes in extern.h because there's no need.
The openssl bit in regress isn't for commit. I did it this way to keep
the diff small if anyone wants to test that. You'll need to build and
install the security/openssl/4.0 and security/openssl/libretls4 ports.
Portable should just work after linking asn1_bit_string.c to the build,
but OpenSSL 4 support needs a little logic to set HAVE_ASN1_BIT_STRING_*
Index: usr.sbin/rpki-client/Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/Makefile,v
diff -u -p -r1.39 Makefile
--- usr.sbin/rpki-client/Makefile 13 Jan 2026 21:36:17 -0000 1.39
+++ usr.sbin/rpki-client/Makefile 13 Apr 2026 09:46:02 -0000
@@ -3,6 +3,7 @@
PROG= rpki-client
SRCS+= as.c
+SRCS+= asn1_bit_string.c
SRCS+= aspa.c
SRCS+= ccr.c
SRCS+= cert.c
Index: usr.sbin/rpki-client/asn1_bit_string.c
===================================================================
RCS file: usr.sbin/rpki-client/asn1_bit_string.c
diff -N usr.sbin/rpki-client/asn1_bit_string.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ usr.sbin/rpki-client/asn1_bit_string.c 13 Apr 2026 09:46:02 -0000
@@ -0,0 +1,96 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2025 Theo Buehler <tb@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <openssl/asn1.h>
+
+#include "extern.h"
+
+/*
+ * My implementation of the OpenSSL 4 API that beck upstreamed in:
+ * https://github.com/openssl/openssl/pull/29926
+ * https://github.com/openssl/openssl/pull/29387
+ */
+
+#ifndef HAVE_ASN1_BIT_STRING_GET_LENGTH
+int
+ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *abs, size_t *out_length,
+ int *out_unused_bits)
+{
+ size_t length;
+ int unused_bits;
+
+ if (abs == NULL || abs->type != V_ASN1_BIT_STRING)
+ return 0;
+
+ if (out_length == NULL || out_unused_bits == NULL)
+ return 0;
+
+ length = abs->length;
+ unused_bits = 0;
+
+ if ((abs->flags & ASN1_STRING_FLAG_BITS_LEFT) != 0)
+ unused_bits = abs->flags & 0x07;
+
+ if (length == 0 && unused_bits != 0)
+ return 0;
+
+ if (unused_bits != 0) {
+ unsigned char mask = (1 << unused_bits) - 1;
+ if ((abs->data[length - 1] & mask) != 0)
+ return 0;
+ }
+
+ *out_length = length;
+ *out_unused_bits = unused_bits;
+
+ return 1;
+}
+#endif /* !HAVE_ASN1_BIT_STRING_GET_LENGTH */
+
+#ifndef HAVE_ASN1_BIT_STRING_SET1
+int
+ASN1_BIT_STRING_set1(ASN1_BIT_STRING *abs, const uint8_t *data, size_t length,
+ int unused_bits)
+{
+ /* OpenSSL likes such checks. */
+ if (abs == NULL)
+ return 0;
+
+ if (length > INT_MAX || unused_bits < 0 || unused_bits > 7)
+ return 0;
+
+ if (length == 0 && unused_bits != 0)
+ return 0;
+
+ if (length > 0 && (data[length - 1] & ((1 << unused_bits) - 1)) != 0)
+ return 0;
+
+ if (!ASN1_STRING_set(abs, data, length))
+ return 0;
+
+ abs->type = V_ASN1_BIT_STRING;
+ abs->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ abs->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits;
+
+ return 1;
+}
+#endif /* HAVE_ASN1_BIT_STRING_SET1 */
Index: usr.sbin/rpki-client/ccr.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/ccr.c,v
diff -u -p -r1.34 ccr.c
--- usr.sbin/rpki-client/ccr.c 7 Apr 2026 14:38:04 -0000 1.34
+++ usr.sbin/rpki-client/ccr.c 13 Apr 2026 09:46:02 -0000
@@ -395,13 +395,9 @@ append_cached_vrp(STACK_OF(ROAIPAddress)
if (num_bits > 0)
unused_bits = 8 - num_bits;
- if (!ASN1_BIT_STRING_set(ripa->address, vrp->addr.addr, num_bytes))
+ if (!ASN1_BIT_STRING_set1(ripa->address, vrp->addr.addr, num_bytes,
+ unused_bits))
errx(1, "ASN1_BIT_STRING_set");
-
- /* ip_addr_parse() handles unused bits, no need to clear them here. */
- ripa->address->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits;
-
- /* XXX - assert that unused bits are zero */
if (vrp->maxlength > vrp->addr.prefixlen) {
if ((ripa->maxLength = ASN1_INTEGER_new()) == NULL)
Index: usr.sbin/rpki-client/extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
diff -u -p -r1.278 extern.h
--- usr.sbin/rpki-client/extern.h 7 Apr 2026 10:59:19 -0000 1.278
+++ usr.sbin/rpki-client/extern.h 13 Apr 2026 09:46:02 -0000
@@ -1024,6 +1024,10 @@ time_t get_current_time(void);
int mkpath(const char *);
int mkpathat(int, const char *);
+/* Compat helpers for OpenSSL < 4 and LibreSSL. */
+int ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *, size_t *, int *);
+int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *, const uint8_t *, size_t, int);
+
#define RPKI_PATH_OUT_DIR "/var/db/rpki-client"
#define RPKI_PATH_BASE_DIR "/var/cache/rpki-client"
Index: usr.sbin/rpki-client/ip.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/ip.c,v
diff -u -p -r1.37 ip.c
--- usr.sbin/rpki-client/ip.c 4 Dec 2025 06:11:44 -0000 1.37
+++ usr.sbin/rpki-client/ip.c 13 Apr 2026 09:46:02 -0000
@@ -173,15 +173,15 @@ ip_addr_parse(const ASN1_BIT_STRING *abs
enum afi afi, const char *fn, struct ip_addr *addr)
{
const unsigned char *data;
- int length, unused = 0;
+ size_t length;
+ int unused = 0;
data = ASN1_STRING_get0_data(abs);
- length = ASN1_STRING_length(abs);
- /* Weird OpenSSL-ism to get unused bit count. */
-
- if ((abs->flags & ASN1_STRING_FLAG_BITS_LEFT))
- unused = abs->flags & 0x07;
+ if (!ASN1_BIT_STRING_get_length(abs, &length, &unused)) {
+ warnx("%s: invalid bit string in IP address", fn);
+ return 0;
+ }
if (length == 0 && unused != 0) {
warnx("%s: RFC 3779 section 2.2.3.8: "
Index: usr.sbin/rpki-client/mft.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/mft.c,v
diff -u -p -r1.136 mft.c
--- usr.sbin/rpki-client/mft.c 16 Jan 2026 11:25:27 -0000 1.136
+++ usr.sbin/rpki-client/mft.c 13 Apr 2026 09:46:02 -0000
@@ -151,7 +151,8 @@ mft_parse_filehash(const char *fn, struc
{
const unsigned char *data;
char *file = NULL;
- int length, rc = 0;
+ size_t len;
+ int length, unused_bits, rc = 0;
struct mftfile *fent;
enum rtype type;
size_t new_idx = 0;
@@ -167,15 +168,19 @@ mft_parse_filehash(const char *fn, struc
if (file == NULL)
err(1, NULL);
- /* XXX - malleability: ensure unused bits are 0. */
data = ASN1_STRING_get0_data(fh->hash);
- length = ASN1_STRING_length(fh->hash);
-
- if (length != SHA256_DIGEST_LENGTH) {
+ if (!ASN1_BIT_STRING_get_length(fh->hash, &len, &unused_bits)) {
warnx("%s: RFC 9286 section 4.2.1: hash: "
"invalid SHA256 length, have %d", fn, length);
goto out;
}
+ if (len != SHA256_DIGEST_LENGTH || unused_bits != 0) {
+ warnx("%s: RFC 9286 section 4.2.1: hash: "
+ "invalid SHA256 length, have %zu (%d unused bits)",
+ fn, len, unused_bits);
+ goto out;
+ }
+ length = len;
type = rtype_from_mftfile(file);
if (type == RTYPE_CRL) {
Index: regress/usr.sbin/rpki-client/Makefile.inc
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/Makefile.inc,v
diff -u -p -r1.46 Makefile.inc
--- regress/usr.sbin/rpki-client/Makefile.inc 16 Jan 2026 11:25:27 -0000 1.46
+++ regress/usr.sbin/rpki-client/Makefile.inc 13 Apr 2026 09:46:02 -0000
@@ -29,7 +29,7 @@ CLEANFILES+= *.out *.err *.txt
TEST_COMMON= as.c aspa.c ccr.c cert.c cms.c constraints-dummy.c crl.c \
encoding.c io.c ip.c json.c mft.c print.c repo-dummy.c \
- rfc3779.c roa.c validate.c x509.c
+ rfc3779.c roa.c validate.c x509.c asn1_bit_string.c
SRCS_test-ip += test-ip.c ${TEST_COMMON}
run-regress-test-ip: test-ip
Index: regress/usr.sbin/rpki-client/openssl/Makefile
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/openssl/Makefile,v
diff -u -p -r1.10 Makefile
--- regress/usr.sbin/rpki-client/openssl/Makefile 31 Mar 2026 06:25:39 -0000 1.10
+++ regress/usr.sbin/rpki-client/openssl/Makefile 13 Apr 2026 09:46:02 -0000
@@ -1,8 +1,10 @@
# $OpenBSD: Makefile,v 1.10 2026/03/31 06:25:39 tb Exp $
-EOPENSSL = eopenssl35
+EOPENSSL = eopenssl40
.if ${EOPENSSL} != "eopenssl35"
+CFLAGS += -DHAVE_ASN1_BIT_STRING_GET_LENGTH
+CFLAGS += -DHAVE_ASN1_BIT_STRING_SET1
CFLAGS += -DHAVE_X509_CRL_GET0_TBS_SIGALG
.endif
Index: regress/usr.sbin/rpki-client/openssl/build/Makefile
===================================================================
RCS file: /cvs/src/regress/usr.sbin/rpki-client/openssl/build/Makefile,v
diff -u -p -r1.7 Makefile
--- regress/usr.sbin/rpki-client/openssl/build/Makefile 31 Mar 2026 06:25:39 -0000 1.7
+++ regress/usr.sbin/rpki-client/openssl/build/Makefile 13 Apr 2026 09:46:02 -0000
@@ -1,8 +1,10 @@
# $OpenBSD: Makefile,v 1.7 2026/03/31 06:25:39 tb Exp $
-EOPENSSL = eopenssl35
+EOPENSSL = eopenssl40
.if ${EOPENSSL} != "eopenssl35"
+CFLAGS += -DHAVE_ASN1_BIT_STRING_GET_LENGTH
+CFLAGS += -DHAVE_ASN1_BIT_STRING_SET1
CFLAGS += -DHAVE_X509_CRL_GET0_TBS_SIGALG
.endif
rpki-client: openssl 4 compat