git: 883ba1bdbe36 - stable/13 - Add 'contrib/libder/' from commit '9c40c4de4c33b2ba1124fb752ebea0bebaa6013f'
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 11 Jan 2025 02:48:40 UTC
The branch stable/13 has been updated by kevans:
URL: https://cgit.FreeBSD.org/src/commit/?id=883ba1bdbe3695579c1b615e90b01ce1bc6a8b14
commit 883ba1bdbe3695579c1b615e90b01ce1bc6a8b14
Author: Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2025-01-01 21:11:02 +0000
Commit: Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2025-01-11 02:48:27 +0000
Add 'contrib/libder/' from commit '9c40c4de4c33b2ba1124fb752ebea0bebaa6013f'
git-subtree-dir: contrib/libder
git-subtree-mainline: d11904b350214943dedb64c7121d4602799d7afd
git-subtree-split: 9c40c4de4c33b2ba1124fb752ebea0bebaa6013f
(cherry picked from commit 35c0a8c449fd2b7f75029ebed5e10852240f0865)
---
contrib/libder/.cirrus.yml | 16 +
contrib/libder/.github/workflows/build.yml | 41 +
contrib/libder/.gitignore | 11 +
contrib/libder/CMakeLists.txt | 28 +
contrib/libder/LICENSE | 22 +
contrib/libder/README.md | 28 +
contrib/libder/derdump/.gitignore | 1 +
contrib/libder/derdump/CMakeLists.txt | 6 +
contrib/libder/derdump/derdump.1 | 51 ++
contrib/libder/derdump/derdump.c | 52 ++
contrib/libder/libder/CMakeLists.txt | 12 +
contrib/libder/libder/libder.3 | 179 +++++
contrib/libder/libder/libder.c | 119 +++
contrib/libder/libder/libder.h | 181 +++++
contrib/libder/libder/libder_error.c | 76 ++
contrib/libder/libder/libder_obj.3 | 138 ++++
contrib/libder/libder/libder_obj.c | 1192 ++++++++++++++++++++++++++++
contrib/libder/libder/libder_private.h | 178 +++++
contrib/libder/libder/libder_read.3 | 101 +++
contrib/libder/libder/libder_read.c | 864 ++++++++++++++++++++
contrib/libder/libder/libder_type.3 | 71 ++
contrib/libder/libder/libder_type.c | 150 ++++
contrib/libder/libder/libder_write.3 | 54 ++
contrib/libder/libder/libder_write.c | 229 ++++++
contrib/libder/tests/.gitignore | 12 +
contrib/libder/tests/CMakeLists.txt | 41 +
contrib/libder/tests/fuzz_parallel.c | 111 +++
contrib/libder/tests/fuzz_stream.c | 246 ++++++
contrib/libder/tests/fuzz_write.c | 79 ++
contrib/libder/tests/fuzzers.h | 40 +
contrib/libder/tests/make_corpus.c | 137 ++++
contrib/libder/tests/repo.priv | Bin 0 -> 64 bytes
contrib/libder/tests/repo.pub | Bin 0 -> 88 bytes
contrib/libder/tests/test_common.h | 29 +
contrib/libder/tests/test_privkey.c | 175 ++++
contrib/libder/tests/test_pubkey.c | 143 ++++
36 files changed, 4813 insertions(+)
diff --git a/contrib/libder/.cirrus.yml b/contrib/libder/.cirrus.yml
new file mode 100644
index 000000000000..a63de71d8bf4
--- /dev/null
+++ b/contrib/libder/.cirrus.yml
@@ -0,0 +1,16 @@
+build_task:
+ matrix:
+ - name: FreeBSD 13
+ freebsd_instance:
+ image: freebsd-13-2-release-amd64
+ - name: FreeBSD 14
+ freebsd_instance:
+ image: freebsd-14-0-release-amd64-ufs
+ setup_script:
+ sudo pkg install -y cmake
+ configure_script:
+ - cmake -B build -DCMAKE_BUILD_TYPE=Debug
+ build_script:
+ make -C build
+ test_script:
+ make -C build check
diff --git a/contrib/libder/.github/workflows/build.yml b/contrib/libder/.github/workflows/build.yml
new file mode 100644
index 000000000000..a10daa25e38f
--- /dev/null
+++ b/contrib/libder/.github/workflows/build.yml
@@ -0,0 +1,41 @@
+name: Build libder
+on:
+ push:
+ branches: ['**']
+ pull_request:
+ types: [opened, reopened, edited, synchronize]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ name: Build ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-20.04, ubuntu-22.04, macos-latest]
+ include:
+ - os: ubuntu-20.04
+ - os: ubuntu-22.04
+ - os: macos-latest
+ steps:
+ - name: checkout
+ uses: actions/checkout@v4
+ - name: install system packages (Ubuntu)
+ if: runner.os == 'Linux'
+ run: |
+ sudo apt-get update --quiet || true
+ sudo apt-get -yq --no-install-suggests --no-install-recommends install cmake
+ - name: install system packages (macOS)
+ if: runner.os == 'macOS'
+ run: |
+ brew update --quiet || true
+ brew install cmake coreutils
+ - name: configure
+ run: |
+ cmake -B build -DCMAKE_BUILD_TYPE=Debug
+ - name: build libder
+ run: make -C build
+ - name: Run self-tests
+ run: make -C build check
diff --git a/contrib/libder/.gitignore b/contrib/libder/.gitignore
new file mode 100644
index 000000000000..34fb4e06c50b
--- /dev/null
+++ b/contrib/libder/.gitignore
@@ -0,0 +1,11 @@
+.*.swp
+.depend*
+*.a
+*.so
+*.so.*
+*.o
+*.pico
+*.debug
+*.full
+
+build/
diff --git a/contrib/libder/CMakeLists.txt b/contrib/libder/CMakeLists.txt
new file mode 100644
index 000000000000..cf0d39e32489
--- /dev/null
+++ b/contrib/libder/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.18)
+
+project(libder)
+
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ if(NOT CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+ add_compile_options(-fsanitize=address,undefined -fstrict-aliasing)
+ add_link_options(-fsanitize=address,undefined -fstrict-aliasing)
+ endif()
+
+ add_compile_options(-Werror)
+endif()
+
+# AppleClang is excluded for the time being; the version used in GitHub Action
+# runners doesn't seem to have that part of libclang_rt installed, though the
+# -fsanitize=fuzzer-no-link instrumentation seems to be fine. Maybe re-evaluate
+# this for MATCHES as a possibility later.
+if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+ set(BUILD_FUZZERS TRUE
+ CACHE BOOL "Build the libFuzzer fuzzers (needs llvm)")
+else()
+ set(BUILD_FUZZERS FALSE
+ CACHE BOOL "Build the libFuzzer fuzzers (needs llvm)")
+endif()
+
+add_subdirectory(libder)
+add_subdirectory(derdump)
+add_subdirectory(tests)
diff --git a/contrib/libder/LICENSE b/contrib/libder/LICENSE
new file mode 100644
index 000000000000..477af8f22e4c
--- /dev/null
+++ b/contrib/libder/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/contrib/libder/README.md b/contrib/libder/README.md
new file mode 100644
index 000000000000..9f700493520d
--- /dev/null
+++ b/contrib/libder/README.md
@@ -0,0 +1,28 @@
+# libder
+
+## What is libder?
+
+libder is a small library for encoding/decoding DER-encoded objects. It is
+expected to be able to decode any BER-encoded buffer, and an attempt to
+re-encode the resulting tree would apply any normalization expected by a DER
+decoder. The author's use is primarily to decode/encode ECC keys for
+interoperability with OpenSSL.
+
+The authoritative source for this software is located at
+https://git.kevans.dev/kevans/libder, but it's additionally mirrored to
+[GitHub](https://github.com/kevans91/libder) for user-facing interactions.
+Pull requests and issues are open on GitHub.
+
+## What is libder not?
+
+libder is not intended to be a general-purpose library for working with DER/BER
+specified objects. It may provide some helpers for building more primitive
+data types, but libder will quickly punt on anything even remotely complex and
+require the library consumer to supply it as a type/payload/size triple that it
+will treat as relatively opaque (modulo some encoding normalization rules that
+can be applied without deeply understanding the data contained within).
+
+libder also doesn't do strict validation of what it reads in today, for better
+or worse. e.g., a boolean may occupy more than one byte and libder will happily
+present it to the application in that way. It would be normalized on
+re-encoding to 0xff or 0x00 depending on whether any bits are set or not.
diff --git a/contrib/libder/derdump/.gitignore b/contrib/libder/derdump/.gitignore
new file mode 100644
index 000000000000..a35adcc4b71d
--- /dev/null
+++ b/contrib/libder/derdump/.gitignore
@@ -0,0 +1 @@
+derdump
diff --git a/contrib/libder/derdump/CMakeLists.txt b/contrib/libder/derdump/CMakeLists.txt
new file mode 100644
index 000000000000..11657426fbc9
--- /dev/null
+++ b/contrib/libder/derdump/CMakeLists.txt
@@ -0,0 +1,6 @@
+file(GLOB derdump_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
+
+add_executable(derdump ${derdump_SOURCES})
+
+target_include_directories(derdump PRIVATE "${CMAKE_SOURCE_DIR}/libder")
+target_link_libraries(derdump der_static)
diff --git a/contrib/libder/derdump/derdump.1 b/contrib/libder/derdump/derdump.1
new file mode 100644
index 000000000000..414799f3055f
--- /dev/null
+++ b/contrib/libder/derdump/derdump.1
@@ -0,0 +1,51 @@
+.\"
+.\" SPDX-Copyright-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (C) 2024 Kyle Evans <kevans@FreeBSD.org>
+.\"
+.Dd March 4, 2024
+.Dt DERDUMP 1
+.Os
+.Sh NAME
+.Nm derdump
+.Nd dumping contents of DER encoded files
+.Sh SYNOPSIS
+.Nm
+.Ar file1
+.Oo Ar fileN ... Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility dumps the contents of one or more DER encoded
+Ar file
+in a more human readable format.
+This is similar to the
+.Xr asn1parse 1
+utility distributed with OpenSSL when used with the
+.Fl inform
+.Ar DER
+option.
+.Pp
+A representation of the object will be output to
+.Em stdout ,
+with indentation to denote objects that are encoded within other constructed
+objects.
+Note that
+.Nm
+does not make much attempt to interpret the contents of any particular object.
+If an object uses one of the universal types, then a friendly name will be
+displayed for that object.
+If an object uses any other type, then
+.Nm
+will display the raw hex value of the type used.
+Values of primitive objects are output as raw hex, and no effort is made to
+try and print a friendly representation.
+.Sh SEE ALSO
+.Xr asn1parse 1 ,
+.Xr libder 3
+.Sh BUGS
+.Nm
+does not currently make any attempt to render a type that uses the long encoded
+format.
+Instead, it will render as
+.Dq { ... } .
diff --git a/contrib/libder/derdump/derdump.c b/contrib/libder/derdump/derdump.c
new file mode 100644
index 000000000000..7ea3768524d8
--- /dev/null
+++ b/contrib/libder/derdump/derdump.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <err.h>
+#include <stdio.h>
+
+#include <libder.h>
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ struct libder_ctx *ctx;
+ struct libder_object *root;
+ size_t rootsz;
+ bool first = true;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s file [file...]\n", argv[0]);
+ return (1);
+ }
+
+ ctx = libder_open();
+ libder_set_verbose(ctx, 2);
+ for (int i = 1; i < argc; i++) {
+ fp = fopen(argv[i], "rb");
+ if (fp == NULL) {
+ warn("%s", argv[i]);
+ continue;
+ }
+
+ if (!first)
+ fprintf(stderr, "\n");
+ fprintf(stdout, "[%s]\n", argv[i]);
+ root = libder_read_file(ctx, fp, &rootsz);
+ if (root != NULL) {
+ libder_obj_dump(root, stdout);
+ libder_obj_free(root);
+ root = NULL;
+ }
+
+ first = false;
+ fclose(fp);
+ }
+
+ libder_close(ctx);
+
+ return (0);
+}
diff --git a/contrib/libder/libder/CMakeLists.txt b/contrib/libder/libder/CMakeLists.txt
new file mode 100644
index 000000000000..8e6f3426d649
--- /dev/null
+++ b/contrib/libder/libder/CMakeLists.txt
@@ -0,0 +1,12 @@
+file(GLOB libder_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
+
+add_library(der SHARED ${libder_SOURCES})
+add_library(der_static STATIC ${libder_SOURCES})
+
+if(BUILD_FUZZERS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
+ target_compile_options(der PUBLIC -fsanitize=fuzzer-no-link)
+ target_link_options(der PUBLIC -fsanitize=fuzzer-no-link)
+
+ target_compile_options(der_static PUBLIC -fsanitize=fuzzer-no-link)
+ target_link_options(der_static PUBLIC -fsanitize=fuzzer-no-link)
+endif()
diff --git a/contrib/libder/libder/libder.3 b/contrib/libder/libder/libder.3
new file mode 100644
index 000000000000..0e06254ef3fb
--- /dev/null
+++ b/contrib/libder/libder/libder.3
@@ -0,0 +1,179 @@
+.\"
+.\" SPDX-Copyright-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (C) 2024 Kyle Evans <kevans@FreeBSD.org>
+.\"
+.Dd March 2, 2024
+.Dt LIBDER 3
+.Os
+.Sh NAME
+.Nm libder ,
+.Nm libder_open ,
+.Nm libder_close ,
+.Nm libder_abort ,
+.Nm libder_get_error ,
+.Nm libder_has_error ,
+.Nm libder_get_normalize ,
+.Nm libder_set_normalize ,
+.Nm libder_get_strict ,
+.Nm libder_set_strict ,
+.Nm libder_get_verbose ,
+.Nm libder_set_verbose
+.Nd DER encoding and decoding library
+.Sh LIBRARY
+.Lb libder
+.Sh SYNOPSIS
+.In libder.h
+.Ft struct libder_ctx *
+.Fn libder_open "void"
+.Ft void
+.Fn libder_close "struct libder_ctx *ctx"
+.Ft void
+.Fn libder_abort "struct libder_ctx *ctx"
+.Ft const char *
+.Fn libder_get_error "struct libder_ctx *ctx"
+.Ft bool
+.Fn libder_has_error "struct libder_ctx *ctx"
+.Ft uint64_t
+.Fn libder_get_normalize "struct libder_ctx *ctx"
+.Ft uint64_t
+.Fn libder_set_normalize "struct libder_ctx *ctx" "uint64_t normalize"
+.Ft bool
+.Fn libder_get_strict "struct libder_ctx *ctx"
+.Ft bool
+.Fn libder_set_strict "struct libder_ctx *ctx" "bool strict"
+.Ft int
+.Fn libder_get_verbose "struct libder_ctx *ctx"
+.Ft int
+.Fn libder_set_verbose "struct libder_ctx *ctx" "int verbose"
+.Sh DESCRIPTION
+The
+.Nm
+library provides functionality for decoding BER and DER encoded data, and
+DER encoding data subjected to constraints outline in ITU-T
+Recommendation X.690.
+.Nm
+will apply relevant normalization rules on write, unless they've been disabled
+with
+.Ft libder_set_normalize ,
+under the assumption that it may not be reading strictly DER encoded data.
+.Pp
+Note that not all of the DER rules are currently implemented.
+.Nm
+will coalesce constructed types that DER specifies should be primitive.
+.Nm
+will primarily normalize bitstrings, booleans, and integers.
+This library was primarily written to be able to provide interoperability with
+OpenSSL keys and signatures, so the library was written with that in mind.
+Eventually it is intended that
+.Nm
+will support the full set of rules, but currently some responsibility is left
+to the library user.
+.Pp
+Also note that
+.Nm
+does not necessarily provide
+.Dq neat
+ways to construct primitives.
+For example, even booleans and integers currently work just by providing a
+buffer that is expected to be formatted in a sane fashion.
+The library user is expected to build the object tree and generally provide the
+object data in a format reasonably encoded as the data for that type should be,
+then
+.Nm
+will provide the proper framing on write and do any transformations that may
+need to be done for strict conformance.
+.Pp
+The
+.Fn libder_open
+function allocates a new
+.Nm
+context.
+The context does not hold any state about any particular structure.
+All of the state held in the context is generally described in this manpage.
+The
+.Fn libder_close
+function will free the context.
+.Pp
+The
+.Fn libder_abort
+function will abort an in-progress
+.Xr libder_read_fd 3
+operation on the existing
+.Fa ctx
+if it is interrupted by a signal in the middle of a
+.Xr read 2
+syscall.
+See
+.Xr libder_read_fd 3
+for further discussion.
+.Pp
+The
+.Fn libder_get_error
+function will return an error string appropriate for the current error, if any.
+The
+.Fn libder_has_error
+function can be used to check if an error was raised in a previous operation.
+.Pp
+The
+.Fn libder_get_normalize
+and
+.Fn libder_set_normalize
+functions retrieve and manipulate any number of flags that detail how
+functions may be used to check or set the normalization flags given
+.Nm context ,
+which dictates how
+.Nm
+will normalize data on write.
+The following normalization flags may be specified:
+.Bl -column "LIBDER_NORMALIZE_CONSTRUCTED"
+.It LIBDER_NORMALIZE_CONSTRUCTED Ta Coalesce types that may be primitive or constructed
+.It LIBDER_NORMALIZE_TAGS Ta Pack tags into the lowest possible encoded value
+.El
+.Pp
+The
+.Fn LIBDER_NORMALIZE_TYPE_FLAG "enum libder_ber_type"
+macaro may also be used to specify normalization of the given universal type.
+By default, every valid normalization flag is enabled.
+.Pp
+The
+.Fn libder_get_strict
+and
+.Fn libder_set_strict
+functions may used to check or set the strict read state of the given
+.Nm
+context.
+By default,
+.Nm
+operates in strict mode and rejects various methods of expressing data that are
+valid looking but not strictly conformant.
+The
+.Va LDE_STRICT_*
+constants in
+.In libder.h
+describe the various scenarios that strict mode may reject.
+.Pp
+The
+.Fn libder_get_verbose
+and
+.Fn libder_set_verbose
+functions may be used to check or set the verbosity of the given
+.Nm
+context.
+This primarily controls how
+.Nm
+behaves when an error is encountered.
+By default, the library will silently set the error state and return.
+With a verbosity level of 1, an error will be printed when the error state is
+set that contains the string that would be returned by
+.Fn libder_get_error .
+With a verbosity level of 2, the filename and line within
+.Nm
+that the error occurred in will be printed, which is primarily intended for
+debugging
+.Nm .
+.Sh SEE ALSO
+.Xr libder_obj 3 ,
+.Xr libder_read 3 ,
+.Xr libder_type 3 ,
+.Xr libder_write 3
diff --git a/contrib/libder/libder/libder.c b/contrib/libder/libder/libder.c
new file mode 100644
index 000000000000..2d52fedd62bd
--- /dev/null
+++ b/contrib/libder/libder/libder.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "libder_private.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * Sets up the context, returns NULL on error.
+ */
+struct libder_ctx *
+libder_open(void)
+{
+ struct libder_ctx *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return (NULL);
+
+ /* Initialize */
+ ctx->error = LDE_NONE;
+ ctx->buffer_size = 0;
+ ctx->verbose = 0;
+ ctx->normalize = LIBDER_NORMALIZE_ALL;
+ ctx->strict = true;
+ ctx->abort = 0;
+
+ return (ctx);
+}
+
+void
+libder_abort(struct libder_ctx *ctx)
+{
+
+ ctx->abort = 1;
+}
+
+LIBDER_PRIVATE size_t
+libder_get_buffer_size(struct libder_ctx *ctx)
+{
+
+ if (ctx->buffer_size == 0) {
+ long psize;
+
+ psize = sysconf(_SC_PAGESIZE);
+ if (psize <= 0)
+ psize = 4096;
+
+ ctx->buffer_size = psize;
+ }
+
+ return (ctx->buffer_size);
+}
+
+uint64_t
+libder_get_normalize(struct libder_ctx *ctx)
+{
+
+ return (ctx->normalize);
+}
+
+/*
+ * Set the normalization flags; returns the previous value.
+ */
+uint64_t
+libder_set_normalize(struct libder_ctx *ctx, uint64_t nmask)
+{
+ uint64_t old = ctx->normalize;
+
+ ctx->normalize = (nmask & LIBDER_NORMALIZE_ALL);
+ return (old);
+}
+
+bool
+libder_get_strict(struct libder_ctx *ctx)
+{
+
+ return (ctx->strict);
+}
+
+bool
+libder_set_strict(struct libder_ctx *ctx, bool strict)
+{
+ bool oval = ctx->strict;
+
+ ctx->strict = strict;
+ return (oval);
+}
+
+int
+libder_get_verbose(struct libder_ctx *ctx)
+{
+
+ return (ctx->verbose);
+}
+
+int
+libder_set_verbose(struct libder_ctx *ctx, int verbose)
+{
+ int oval = ctx->verbose;
+
+ ctx->verbose = verbose;
+ return (oval);
+}
+
+void
+libder_close(struct libder_ctx *ctx)
+{
+
+ if (ctx == NULL)
+ return;
+
+ free(ctx);
+}
+
diff --git a/contrib/libder/libder/libder.h b/contrib/libder/libder/libder.h
new file mode 100644
index 000000000000..4d28aa3052ba
--- /dev/null
+++ b/contrib/libder/libder/libder.h
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+
+enum libder_ber_class {
+ BC_UNIVERSAL = 0,
+ BC_APPLICATION = 1,
+ BC_CONTEXT = 2,
+ BC_PRIVATE = 3,
+};
+
+enum libder_ber_type {
+ BT_RESERVED = 0x00,
+ BT_BOOLEAN = 0x01,
+ BT_INTEGER = 0x02,
+ BT_BITSTRING = 0x03,
+ BT_OCTETSTRING = 0x04,
+ BT_NULL = 0x05,
+ BT_OID = 0x06,
+ BT_OBJDESC = 0x07,
+ BT_EXTERNAL = 0x08,
+ BT_REAL = 0x09,
+ BT_ENUMERATED = 0x0a,
+ BT_PDV = 0x0b,
+ BT_UTF8STRING = 0x0c,
+ BT_RELOID = 0x0d,
+
+ /* 0x10, 011 not usable */
+
+ BT_NUMERICSTRING = 0x012,
+ BT_STRING = 0x13,
+ BT_TELEXSTRING = 0x14,
+ BT_VIDEOTEXSTRING = 0x15,
+ BT_IA5STRING = 0x16,
+ BT_UTCTIME = 0x17,
+ BT_GENTIME = 0x18,
+ BT_GFXSTRING = 0x19,
+ BT_VISSTRING = 0x1a,
+ BT_GENSTRING = 0x1b,
+ BT_UNIVSTRING = 0x1c,
+ BT_CHARSTRING = 0x1d,
+ BT_BMPSTRING = 0x1e,
+
+ BT_SEQUENCE = 0x30,
+ BT_SET = 0x31,
+};
+
+#define BER_TYPE_CONSTRUCTED_MASK 0x20 /* Bit 6 */
+#define BER_TYPE_CLASS_MASK 0xc0 /* Bits 7 and 8 */
+
+/*
+ * The difference between the type and the full type is just that the full type
+ * will indicate the class of type, so it may be more useful for some operations.
+ */
+#define BER_FULL_TYPE(tval) \
+ ((tval) & ~(BER_TYPE_CONSTRUCTED_MASK))
+#define BER_TYPE(tval) \
+ ((tval) & ~(BER_TYPE_CLASS_MASK | BER_TYPE_CONSTRUCTED_MASK))
+#define BER_TYPE_CLASS(tval) \
+ (((tval) & BER_TYPE_CLASS_MASK) >> 6)
+#define BER_TYPE_CONSTRUCTED(tval) \
+ (((tval) & BER_TYPE_CONSTRUCTED_MASK) != 0)
+
+enum libder_error {
+ LDE_NONE = 0x00,
+ LDE_NOMEM, /* Out of memory */
+ LDE_INVAL, /* Invalid parameter */
+ LDE_SHORTHDR, /* Header too short */
+ LDE_BADVARLEN, /* Bad variable length encoding */
+ LDE_LONGLEN, /* Encoded length too large (8 byte max) */
+ LDE_SHORTDATA, /* Payload not available */
+ LDE_GARBAGE, /* Garbage after encoded data */
+ LDE_STREAMERR, /* Stream error */
+ LDE_TRUNCVARLEN, /* Variable length object truncated */
+ LDE_COALESCE_BADCHILD, /* Bad child encountered when coalescing */
+ LDE_BADOBJECT, /* Payload not valid for object type */
+
+ /* Strict violations */
+ LDE_STRICT_EOC, /* Strict: end-of-content violation */
+ LDE_STRICT_TAG, /* Strict: tag violation */
+ LDE_STRICT_PVARLEN, /* Strict: primitive using indefinite length */
+ LDE_STRICT_BOOLEAN, /* Strict: boolean encoded incorrectly */
+ LDE_STRICT_NULL, /* Strict: null encoded incorrectly */
+ LDE_STRICT_PRIMITIVE, /* Strict: type must be primitive */
+ LDE_STRICT_CONSTRUCTED, /* Strict: type must be constructed */
+ LDE_STRICT_BITSTRING, /* Strict: malformed constructed bitstring */
+};
+
+struct libder_ctx;
+struct libder_tag;
+struct libder_object;
+
+/*
+ * By default we normalize everything, but we allow some subset of the
+ * functionality to be disabled. Lengths are non-optional and will always be
+ * normalized to a fixed short or long length. The upper 32-bits of
+ * ctx->normalize are reserved for universal types so that we can quickly map
+ * those without assigning them names.
+ */
+
+/* Normalize constructed types that should be coalesced (e.g., strings, time). */
+#define LIBDER_NORMALIZE_CONSTRUCTED 0x0000000000000001ULL
+
+/*
+ * Normalize tags on read. This is mostly a measure to ensure that
+ * normalization on write doesn't get thwarted; there's no reason anybody should
+ * be encoding low tags with the long form, but the spec doesn't appear to
+ * forbid it.
+ */
+#define LIBDER_NORMALIZE_TAGS 0x0000000000000002ULL
+
+/* Universal types (reserved) */
+#define LIBDER_NORMALIZE_TYPE_MASK 0xffffffff00000000ULL
+#define LIBDER_NORMALIZE_TYPE_FLAG(val) ((1ULL << val) << 32ULL)
+
+/* All valid bits. */
+#define LIBDER_NORMALIZE_ALL \
+ (LIBDER_NORMALIZE_TYPE_MASK | LIBDER_NORMALIZE_CONSTRUCTED | \
+ LIBDER_NORMALIZE_TAGS)
+
+struct libder_ctx * libder_open(void);
+void libder_close(struct libder_ctx *);
+void libder_abort(struct libder_ctx *);
+const char *libder_get_error(struct libder_ctx *);
+bool libder_has_error(struct libder_ctx *);
+uint64_t libder_get_normalize(struct libder_ctx *);
+uint64_t libder_set_normalize(struct libder_ctx *, uint64_t);
+bool libder_get_strict(struct libder_ctx *);
+bool libder_set_strict(struct libder_ctx *, bool);
+int libder_get_verbose(struct libder_ctx *);
+int libder_set_verbose(struct libder_ctx *, int);
+
+struct libder_object *libder_read(struct libder_ctx *, const uint8_t *, size_t *);
+struct libder_object *libder_read_fd(struct libder_ctx *, int, size_t *);
+struct libder_object *libder_read_file(struct libder_ctx *, FILE *, size_t *);
+
+uint8_t *libder_write(struct libder_ctx *, struct libder_object *, uint8_t *,
+ size_t *);
+
+#define DER_CHILDREN(obj) libder_obj_children(obj)
+#define DER_NEXT(obj) libder_obj_next(obj)
+
+#define DER_FOREACH_CHILD(var, obj) \
+ for ((var) = DER_CHILDREN((obj)); \
+ (var); \
+ (var) = DER_NEXT((var)))
+#define DER_FOREACH_CHILD_SAFE(var, obj, tvar) \
+ for ((var) = DER_CHILDREN((obj)); \
+ (var) && ((tvar) = DER_NEXT((var)), 1); \
+ (var) = (tvar))
+
+struct libder_object *libder_obj_alloc(struct libder_ctx *, struct libder_tag *, const uint8_t *, size_t);
+struct libder_object *libder_obj_alloc_simple(struct libder_ctx *, uint8_t, const uint8_t *,
+ size_t);
+void libder_obj_free(struct libder_object *);
+
+bool libder_obj_append(struct libder_object *, struct libder_object *);
+struct libder_object *libder_obj_child(const struct libder_object *, size_t);
+struct libder_object *libder_obj_children(const struct libder_object *);
+struct libder_object *libder_obj_next(const struct libder_object *);
+struct libder_tag *libder_obj_type(const struct libder_object *);
+uint8_t libder_obj_type_simple(const struct libder_object *);
+const uint8_t *libder_obj_data(const struct libder_object *, size_t *);
+
+/* Debugging aide -- probably shouldn't use. */
+void libder_obj_dump(const struct libder_object *, FILE *);
+
+struct libder_tag *libder_type_alloc_simple(struct libder_ctx *, uint8_t);
+struct libder_tag *libder_type_dup(struct libder_ctx *, const struct libder_tag *);
+void libder_type_free(struct libder_tag *);
+#define libder_type_simple libder_type_simple_abi
+uint8_t libder_type_simple(const struct libder_tag *);
diff --git a/contrib/libder/libder/libder_error.c b/contrib/libder/libder/libder_error.c
new file mode 100644
index 000000000000..6ca0acc83e6d
--- /dev/null
+++ b/contrib/libder/libder/libder_error.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <stdio.h>
+
+#include "libder_private.h"
+
+#undef libder_set_error
+
+static const char libder_error_nodesc[] = "[Description not available]";
+
+#define DESCRIBE(err, msg) { LDE_ ## err, msg }
+static const struct libder_error_desc {
+ enum libder_error desc_error;
+ const char *desc_str;
+} libder_error_descr[] = {
+ DESCRIBE(NONE, "No error"),
+ DESCRIBE(NOMEM, "Out of memory"),
+ DESCRIBE(INVAL, "Invalid parameter"),
+ DESCRIBE(SHORTHDR, "Header too short"),
+ DESCRIBE(BADVARLEN, "Bad variable length encoding"),
+ DESCRIBE(LONGLEN, "Encoded length too large (8 byte max)"),
+ DESCRIBE(SHORTDATA, "Payload not available (too short)"),
+ DESCRIBE(GARBAGE, "Garbage after encoded data"),
+ DESCRIBE(STREAMERR, "Stream error"),
+ DESCRIBE(TRUNCVARLEN, "Variable length object truncated"),
+ DESCRIBE(COALESCE_BADCHILD, "Bad child encountered when coalescing"),
+ DESCRIBE(BADOBJECT, "Payload not valid for object type"),
+ DESCRIBE(STRICT_EOC, "Strict: end-of-content violation"),
+ DESCRIBE(STRICT_TAG, "Strict: tag violation"),
+ DESCRIBE(STRICT_PVARLEN, "Strict: primitive using indefinite length"),
+ DESCRIBE(STRICT_BOOLEAN, "Strict: boolean encoded incorrectly"),
+ DESCRIBE(STRICT_NULL, "Strict: null encoded incorrectly"),
+ DESCRIBE(STRICT_PRIMITIVE, "Strict: type must be primitive"),
+ DESCRIBE(STRICT_CONSTRUCTED, "Strict: type must be constructed"),
+ DESCRIBE(STRICT_BITSTRING, "Strict: malformed constructed bitstring"),
+};
+
+const char *
+libder_get_error(struct libder_ctx *ctx)
+{
+ const struct libder_error_desc *desc;
+
+ for (size_t i = 0; i < nitems(libder_error_descr); i++) {
+ desc = &libder_error_descr[i];
+
+ if (desc->desc_error == ctx->error)
+ return (desc->desc_str);
+ }
+
+ return (libder_error_nodesc);
+}
+
+bool
+libder_has_error(struct libder_ctx *ctx)
+{
+
+ return (ctx->error != 0);
+}
+
+LIBDER_PRIVATE void
+libder_set_error(struct libder_ctx *ctx, int error, const char *file, int line)
+{
+ ctx->error = error;
+
+ if (ctx->verbose >= 2) {
+ fprintf(stderr, "%s: [%s:%d]: %s (error %d)\n",
+ __func__, file, line, libder_get_error(ctx), error);
+ } else if (ctx->verbose >= 1) {
+ fprintf(stderr, "%s: %s (error %d)\n", __func__,
+ libder_get_error(ctx), error);
+ }
+}
diff --git a/contrib/libder/libder/libder_obj.3 b/contrib/libder/libder/libder_obj.3
new file mode 100644
index 000000000000..d7e51da1d2fb
--- /dev/null
+++ b/contrib/libder/libder/libder_obj.3
@@ -0,0 +1,138 @@
+.\"
+.\" SPDX-Copyright-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (C) 2024 Kyle Evans <kevans@FreeBSD.org>
+.\"
+.Dd March 2, 2024
+.Dt LIBDER_OBJ 3
+.Os
+.Sh NAME
+.Nm libder_obj ,
+.Nm libder_obj_alloc ,
+.Nm libder_obj_alloc_simple ,
*** 4094 LINES SKIPPED ***