Download raw body.
rpki-client: openssl 4 compat
On Mon, Apr 13, 2026 at 12:11:38PM +0200, Theo Buehler wrote:
> 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
>
Looks good to me.
> + /* OpenSSL likes such checks. */
Hell yes. I would have skipped a fair bunch of those but it seems people
like to pass random NULLs everywhere.
OK claudio@
--
:wq Claudio
rpki-client: openssl 4 compat