apk: add package
authorPaul Spooren <mail@aparcar.org>
Sun, 17 Mar 2024 17:11:48 +0000 (18:11 +0100)
committerPaul Spooren <mail@aparcar.org>
Mon, 29 Apr 2024 09:36:03 +0000 (11:36 +0200)
APK (Alpine Package Keeper) is the package manager of Alpine Linux and
has multiple advantages over OPKG. While Alpine uses APK version 2, this
commit adds version 3 with a heavily optimised database structure and
additional feature making it suitable for OpenWrt.

This commit will be followed by many more to add APK build capabilities
to the OpenWrt build system, firstly enabling side by side builds of APK
and OPKG packages, later replacing OPKG entirely.

Signed-off-by: Paul Spooren <mail@aparcar.org>
package/system/apk/Makefile [new file with mode: 0644]
package/system/apk/patches/0001-openwrt-move-layer-db-to-temp-folder.patch [new file with mode: 0644]
package/system/apk/patches/0002-mbedtls-support.patch [new file with mode: 0644]

diff --git a/package/system/apk/Makefile b/package/system/apk/Makefile
new file mode 100644 (file)
index 0000000..912ddc2
--- /dev/null
@@ -0,0 +1,87 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=apk
+PKG_RELEASE:=1
+
+PKG_SOURCE_URL=https://gitlab.alpinelinux.org/alpine/apk-tools.git
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_DATE:=2024-04-16
+PKG_SOURCE_VERSION:=ba6c31a5469ef74fb85119508e55de9631ffef41
+PKG_MIRROR_HASH:=3455d5799481add9ece3db685576d58be6303f3a13140133979b965cbd3c9966
+
+PKG_VERSION=3.0.0_pre$(subst -,,$(PKG_SOURCE_DATE))
+
+PKG_MAINTAINER:=Paul Spooren <mail@aparcar.org>
+PKG_LICENSE:=GPL-2.0-only
+PKG_LICENSE_FILES:=LICENSE
+PKG_INSTALL:=1
+
+HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST)
+HOST_BUILD_DEPENDS:=lua/host
+PKG_BUILD_DEPENDS:=$(HOST_BUILD_DEPENDS)
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/host-build.mk
+include $(INCLUDE_DIR)/meson.mk
+
+define Package/apk/default
+  SECTION:=base
+  CATEGORY:=Base system
+  TITLE:=apk package manager
+  DEPENDS:=+zlib
+  URL:=$(PKG_SOURCE_URL)
+endef
+
+define Package/apk-mbedtls
+  $(Package/apk/default)
+  TITLE += (mbedtls)
+  DEPENDS +=+libmbedtls
+  VARIANT:=mbedtls
+  DEFAULT_VARIANT:=1
+  CONFLICTS:=apk-openssl
+endef
+
+define Package/apk-openssl
+  $(Package/apk/default)
+  TITLE += (openssl)
+  DEPENDS +=+libopenssl
+  VARIANT:=openssl
+endef
+
+MESON_HOST_VARS+=VERSION=$(PKG_VERSION)
+MESON_VARS+=VERSION=$(PKG_VERSION)
+
+MESON_HOST_ARGS += \
+       -Dlua_version=5.1 \
+       -Dcompressed-help=false \
+       -Ddocs=disabled \
+       -Dcrypto_backend=openssl \
+       -Dzstd=false
+
+MESON_ARGS += \
+       -Dlua_version=5.1 \
+       -Dcompressed-help=false \
+       -Ddocs=disabled \
+       -Durl_backend=wget \
+       -Dcrypto_backend=$(BUILD_VARIANT) \
+       -Dzstd=false
+
+HOST_LDFLAGS += \
+       -Wl,-rpath $(STAGING_DIR_HOST)/lib
+
+define Package/apk/default/install
+       $(INSTALL_DIR) $(1)/lib/apk/db
+
+       $(INSTALL_DIR) $(1)/usr/bin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/apk $(1)/usr/bin/apk
+
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(CP) $(PKG_INSTALL_DIR)/usr/lib/libapk.so.* $(1)/usr/lib/
+endef
+
+Package/apk-mbedtls/install = $(Package/apk/default/install)
+Package/apk-openssl/install = $(Package/apk/default/install)
+
+$(eval $(call BuildPackage,apk-mbedtls))
+$(eval $(call BuildPackage,apk-openssl))
+$(eval $(call HostBuild))
diff --git a/package/system/apk/patches/0001-openwrt-move-layer-db-to-temp-folder.patch b/package/system/apk/patches/0001-openwrt-move-layer-db-to-temp-folder.patch
new file mode 100644 (file)
index 0000000..eac8a96
--- /dev/null
@@ -0,0 +1,21 @@
+From 9918c683fcc2f148328332d58d030ec5750a1473 Mon Sep 17 00:00:00 2001
+From: Paul Spooren <mail@aparcar.org>
+Date: Sat, 19 Feb 2022 17:20:37 +0100
+Subject: [PATCH 1/4] openwrt: move layer db to temp folder
+
+Signed-off-by: Paul Spooren <mail@aparcar.org>
+---
+ src/database.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/src/database.c
++++ b/src/database.c
+@@ -1604,7 +1604,7 @@ const char *apk_db_layer_name(int layer)
+ {
+       switch (layer) {
+       case APK_DB_LAYER_ROOT: return "lib/apk/db";
+-      case APK_DB_LAYER_UVOL: return "lib/apk/db-uvol";
++      case APK_DB_LAYER_UVOL: return "tmp/run/uvol/.meta/apk";
+       default:
+               assert("invalid layer");
+               return 0;
diff --git a/package/system/apk/patches/0002-mbedtls-support.patch b/package/system/apk/patches/0002-mbedtls-support.patch
new file mode 100644 (file)
index 0000000..62b3ab8
--- /dev/null
@@ -0,0 +1,917 @@
+From 74ea482102e1a7c1845b3eec19cbdb21264836d4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Fri, 5 Apr 2024 12:06:56 +0300
+Subject: [PATCH 1/4] add alternate url wget implementation
+
+---
+ .gitlab-ci.yml    |  16 ++++-
+ meson.build       |   6 +-
+ meson_options.txt |   1 +
+ src/io_url_wget.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++
+ src/meson.build   |   4 +-
+ 5 files changed, 173 insertions(+), 4 deletions(-)
+ create mode 100644 src/io_url_wget.c
+
+diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
+index 7fc86563..b7e00008 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -24,7 +24,19 @@ test:alpine:
+     script:
+         - apk update
+         - apk add make gcc git musl-dev openssl-dev linux-headers zlib-dev zstd-dev lua5.3-dev lua5.3-lzlib meson zlib-static zstd-static openssl-libs-static
+-        - meson build
++        - meson setup build -Dstatic_apk=true
++        - ninja -C build
++    tags:
++        - docker-alpine
++        - x86_64
++
++test:alpine-alt-config:
++    image: alpine
++    stage: test
++    script:
++        - apk update
++        - apk add make gcc git musl-dev openssl-dev linux-headers zlib-dev lua5.3-dev lua5.3-lzlib meson
++        - meson setup build -Durl_backend=wget -Dzstd=false
+         - ninja -C build
+     tags:
+         - docker-alpine
+@@ -38,7 +50,7 @@ test:debian:
+         - apt-get install -y make gcc git libssl-dev zlib1g-dev libzstd-dev lua5.3-dev lua5.2 lua-zlib-dev sudo meson
+         - unlink /bin/sh
+         - ln -s /bin/bash /bin/sh
+-        - meson build
++        - meson setup build
+         - ninja -C build
+     tags:
+         - docker-alpine
+diff --git a/meson.build b/meson.build
+index 1a44c11f..9a14cac0 100644
+--- a/meson.build
++++ b/meson.build
+@@ -33,6 +33,10 @@ subproject = meson.is_subproject()
+ subdir('doc')
+ subdir('portability')
+-subdir('libfetch')
++if get_option('url_backend') == 'libfetch'
++      subdir('libfetch')
++else
++      libfetch_dep = dependency('', required: false)
++endif
+ subdir('src')
+ subdir('tests')
+diff --git a/meson_options.txt b/meson_options.txt
+index 693f46ec..940fe9a4 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -5,5 +5,6 @@ option('help', description: 'Build help into apk binaries, needs lua', type: 'fe
+ option('lua', description: 'Build luaapk (lua bindings)', type: 'feature', value: 'auto')
+ option('lua_version', description: 'Lua version to build against', type: 'string', value: '5.3')
+ option('static_apk', description: 'Also build apk.static', type: 'boolean', value: false)
++option('url_backend', description: 'URL backend', type: 'combo', choices: ['libfetch', 'wget'], value: 'libfetch')
+ option('uvol_db_target', description: 'Default target for uvol database layer', type: 'string')
+ option('zstd', description: 'Build with zstd support', type: 'boolean', value: true)
+diff --git a/src/io_url_wget.c b/src/io_url_wget.c
+new file mode 100644
+index 00000000..9a929222
+--- /dev/null
++++ b/src/io_url_wget.c
+@@ -0,0 +1,150 @@
++/* io_url_wget.c - Alpine Package Keeper (APK)
++ *
++ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
++ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
++ * All rights reserved.
++ *
++ * SPDX-License-Identifier: GPL-2.0-only
++ */
++
++#include <spawn.h>
++#include <unistd.h>
++#include <sys/wait.h>
++#include "apk_io.h"
++
++static char wget_timeout[16];
++static char wget_no_check_certificate;
++
++static int wget_translate_status(int status)
++{
++      if (!WIFEXITED(status)) return -EFAULT;
++      switch (WEXITSTATUS(status)) {
++      case 0: return 0;
++      case 3: return -EIO;
++      case 4: return -ENETUNREACH;
++      case 5: return -EACCES;
++      case 6: return -EACCES;
++      case 7: return -EPROTO;
++      default: return -APKE_REMOTE_IO;
++      }
++}
++
++struct apk_wget_istream {
++      struct apk_istream is;
++      int fd;
++      pid_t pid;
++};
++
++static int wget_spawn(const char *url, pid_t *pid, int *fd)
++{
++      int i = 0, r, pipefds[2];
++      posix_spawn_file_actions_t act;
++      char *argv[16];
++
++      argv[i++] = "wget";
++      argv[i++] = "-q";
++      argv[i++] = "-T";
++      argv[i++] = wget_timeout;
++      if (wget_no_check_certificate) argv[i++] = "--no-check-certificate";
++      argv[i++] = (char *) url;
++      argv[i++] = "-O";
++      argv[i++] = "-";
++      argv[i++] = 0;
++
++      if (pipe2(pipefds, O_CLOEXEC) != 0) return -errno;
++
++      posix_spawn_file_actions_init(&act);
++      posix_spawn_file_actions_adddup2(&act, pipefds[1], STDOUT_FILENO);
++      r = posix_spawnp(pid, "wget", &act, 0, argv, environ);
++      posix_spawn_file_actions_destroy(&act);
++      if (r != 0) return -r;
++      close(pipefds[1]);
++      *fd = pipefds[0];
++      return 0;
++}
++
++static int wget_check_exit(struct apk_wget_istream *wis)
++{
++      int status;
++
++      if (wis->pid == 0) return apk_istream_error(&wis->is, 0);
++      if (waitpid(wis->pid, &status, 0) == wis->pid) {
++              wis->pid = 0;
++              return apk_istream_error(&wis->is, wget_translate_status(status));
++      }
++      return 0;
++}
++
++static void wget_get_meta(struct apk_istream *is, struct apk_file_meta *meta)
++{
++}
++
++static ssize_t wget_read(struct apk_istream *is, void *ptr, size_t size)
++{
++      struct apk_wget_istream *wis = container_of(is, struct apk_wget_istream, is);
++      ssize_t r;
++
++      r = read(wis->fd, ptr, size);
++      if (r < 0) return -errno;
++      if (r == 0) return wget_check_exit(wis);
++      return r;
++}
++
++static int wget_close(struct apk_istream *is)
++{
++      int r = is->err;
++      struct apk_wget_istream *wis = container_of(is, struct apk_wget_istream, is);
++
++      while (wis->pid != 0)
++              wget_check_exit(wis);
++
++      close(wis->fd);
++      free(wis);
++      return r < 0 ? r : 0;
++}
++
++static const struct apk_istream_ops wget_istream_ops = {
++      .get_meta = wget_get_meta,
++      .read = wget_read,
++      .close = wget_close,
++};
++
++struct apk_istream *apk_io_url_istream(const char *url, time_t since)
++{
++      struct apk_wget_istream *wis;
++      int r;
++
++      wis = malloc(sizeof(*wis) + apk_io_bufsize);
++      if (wis == NULL) return ERR_PTR(-ENOMEM);
++
++      *wis = (struct apk_wget_istream) {
++              .is.ops = &wget_istream_ops,
++              .is.buf = (uint8_t *)(wis + 1),
++              .is.buf_size = apk_io_bufsize,
++      };
++      r = wget_spawn(url, &wis->pid, &wis->fd);
++      if (r != 0) {
++              free(wis);
++              return ERR_PTR(r);
++      }
++
++      return &wis->is;
++}
++
++void apk_io_url_no_check_certificate(void)
++{
++      wget_no_check_certificate = 1;
++}
++
++void apk_io_url_set_timeout(int timeout)
++{
++      snprintf(wget_timeout, sizeof wget_timeout, "%d", timeout);
++}
++
++void apk_io_url_set_redirect_callback(void (*cb)(int, const char *))
++{
++}
++
++void apk_io_url_init(void)
++{
++}
+diff --git a/src/meson.build b/src/meson.build
+index c1aae550..38e9d3b0 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -1,3 +1,5 @@
++url_backend = get_option('url_backend')
++
+ libapk_so_version = '2.99.0'
+ libapk_src = [
+       'adb.c',
+@@ -22,8 +24,8 @@ libapk_src = [
+       'fs_uvol.c',
+       'hash.c',
+       'io.c',
+-      'io_url_libfetch.c',
+       'io_gunzip.c',
++      'io_url_@0@.c'.format(url_backend),
+       'package.c',
+       'pathbuilder.c',
+       'print.c',
+-- 
+GitLab
+
+
+From b9fe78fbf19bb10e1d0b8eb1cb1de123bee2ed7e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 16 Apr 2024 17:55:15 +0200
+Subject: [PATCH 2/4] add option to configure url backend in legacy make build
+ system
+
+Can be configured by setting URL_BACKEND. If not set libfetch is
+selected by default.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ src/Makefile | 20 ++++++++++++++------
+ 1 file changed, 14 insertions(+), 6 deletions(-)
+
+diff --git a/src/Makefile b/src/Makefile
+index f7873cb1..efdc68df 100644
+--- a/src/Makefile
++++ b/src/Makefile
+@@ -9,8 +9,8 @@ else
+ $(error Lua interpreter not found. Please specify LUA interpreter, or use LUA=no to build without help.)
+ endif
+-OPENSSL_CFLAGS                := $(shell $(PKG_CONFIG) --cflags openssl)
+-OPENSSL_LIBS          := $(shell $(PKG_CONFIG) --libs openssl)
++OPENSSL_CFLAGS         := $(shell $(PKG_CONFIG) --cflags openssl)
++OPENSSL_LIBS           := $(shell $(PKG_CONFIG) --libs openssl)
+ ZLIB_CFLAGS           := $(shell $(PKG_CONFIG) --cflags zlib)
+ ZLIB_LIBS             := $(shell $(PKG_CONFIG) --libs zlib)
+@@ -21,10 +21,18 @@ libapk_so          := $(obj)/libapk.so.$(libapk_soname)
+ libapk.so.$(libapk_soname)-objs := \
+       adb.o adb_comp.o adb_walk_adb.o adb_walk_genadb.o adb_walk_gentext.o adb_walk_text.o apk_adb.o \
+       atom.o blob.o commit.o common.o context.o crypto.o crypto_openssl.o ctype.o database.o hash.o \
+-      extract_v2.o extract_v3.o fs_fsys.o fs_uvol.o io.o io_gunzip.o io_url_libfetch.o \
+-      tar.o package.o pathbuilder.o print.o solver.o trust.o version.o
++      extract_v2.o extract_v3.o fs_fsys.o fs_uvol.o io.o io_gunzip.o tar.o package.o pathbuilder.o \
++      print.o solver.o trust.o version.o
+-libapk.so.$(libapk_soname)-libs := libfetch/libfetch.a
++libapk.so.$(libapk_soname)-libs :=
++
++ifeq ($(URL_BACKEND),wget)
++libapk.so.$(libapk_soname)-objs += io_url_wget.o
++else
++CFLAGS_ALL += -Ilibfetch
++libapk.so.$(libapk_soname)-objs += io_url_libfetch.o
++libapk.so.$(libapk_soname)-libs += libfetch/libfetch.a
++endif
+ # ZSTD support can be disabled
+ ifneq ($(ZSTD),no)
+@@ -79,7 +87,7 @@ LIBS_apk             := -lapk
+ LIBS_apk-test         := -lapk
+ LIBS_apk.so           := -L$(obj) -lapk
+-CFLAGS_ALL            += -D_ATFILE_SOURCE -Ilibfetch -Iportability
++CFLAGS_ALL            += -D_ATFILE_SOURCE -Iportability
+ CFLAGS_apk.o          := -DAPK_VERSION=\"$(VERSION)\"
+ CFLAGS_apk-static.o   := -DAPK_VERSION=\"$(VERSION)\" -DOPENSSL_NO_ENGINE
+ CFLAGS_apk-test.o     := -DAPK_VERSION=\"$(VERSION)\" -DOPENSSL_NO_ENGINE -DTEST_MODE
+-- 
+GitLab
+
+
+From 0418b684898403c49905c1f0e4b7c5ca522b2d50 Mon Sep 17 00:00:00 2001
+From: Jonas Jelonek <jelonek.jonas@gmail.com>
+Date: Sun, 14 Apr 2024 00:20:14 +0200
+Subject: [PATCH 3/4] crypto: add support for mbedtls as backend
+
+backend is selected at compile-time with crypto_backend option
+
+Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
+---
+ libfetch/meson.build     |   2 +-
+ meson.build              |  14 +-
+ meson_options.txt        |   1 +
+ portability/getrandom.c  |  19 +++
+ portability/meson.build  |   3 +-
+ portability/sys/random.h |   6 +
+ src/apk_crypto.h         |   5 +
+ src/apk_crypto_mbedtls.h |  30 +++++
+ src/crypto_mbedtls.c     | 285 +++++++++++++++++++++++++++++++++++++++
+ src/meson.build          |  21 ++-
+ 10 files changed, 373 insertions(+), 13 deletions(-)
+ create mode 100644 portability/getrandom.c
+ create mode 100644 portability/sys/random.h
+ create mode 100644 src/apk_crypto_mbedtls.h
+ create mode 100644 src/crypto_mbedtls.c
+
+diff --git a/libfetch/meson.build b/libfetch/meson.build
+index 431ba197..e24f95eb 100644
+--- a/libfetch/meson.build
++++ b/libfetch/meson.build
+@@ -40,7 +40,7 @@ libfetch = static_library(
+       c_args: libfetch_cargs,
+       dependencies: [
+               libportability_dep.partial_dependency(compile_args: true, includes: true),
+-              openssl_dep.partial_dependency(compile_args: true, includes: true)
++              crypto_dep.partial_dependency(compile_args: true, includes: true)
+       ],
+ )
+diff --git a/meson.build b/meson.build
+index 9a14cac0..3a83f4e1 100644
+--- a/meson.build
++++ b/meson.build
+@@ -13,15 +13,21 @@ apk_libdir = get_option('libdir')
+ lua_bin = find_program('lua' + get_option('lua_version'), required: get_option('help'))
+ lua_dep = dependency('lua' + get_option('lua_version'), required: get_option('lua'))
+ scdoc_dep = dependency('scdoc', version: '>=1.10', required: get_option('docs'))
+-openssl_dep = dependency('openssl')
+-openssl_static_dep = dependency('openssl', static: true)
+ zlib_dep = dependency('zlib')
+ zlib_static_dep = dependency('zlib', static: true)
+ libzstd_dep = dependency('libzstd', required: get_option('zstd'))
+ libzstd_static_dep = dependency('libzstd', required: get_option('zstd'), static: true)
+-shared_deps = [ openssl_dep, zlib_dep, libzstd_dep ]
+-static_deps = [ openssl_static_dep, zlib_static_dep, libzstd_static_dep ]
++if get_option('crypto_backend') == 'openssl'
++      crypto_dep = dependency('openssl')
++      crypto_static_dep = dependency('openssl', static: true)
++elif get_option('crypto_backend') == 'mbedtls'
++      crypto_dep = [ dependency('mbedtls'), dependency('mbedcrypto') ]
++      crypto_static_dep = [ dependency('mbedtls', static: true), dependency('mbedcrypto', static: true) ]
++endif
++
++shared_deps = [ crypto_dep, zlib_dep, libzstd_dep ]
++static_deps = [ crypto_static_dep, zlib_static_dep, libzstd_static_dep ]
+ add_project_arguments('-D_GNU_SOURCE', language: 'c')
+diff --git a/meson_options.txt b/meson_options.txt
+index 940fe9a4..df0b07dc 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -1,4 +1,5 @@
+ option('arch_prefix', description: 'Define a custom arch prefix for default arch', type: 'string')
++option('crypto_backend', description: 'Crypto backend', type: 'combo', choices: ['openssl', 'mbedtls'], value: 'openssl')
+ option('compressed-help', description: 'Compress help database, needs lua-zlib', type: 'boolean', value: true)
+ option('docs', description: 'Build manpages with scdoc', type: 'feature', value: 'auto')
+ option('help', description: 'Build help into apk binaries, needs lua', type: 'feature', value: 'auto')
+diff --git a/portability/getrandom.c b/portability/getrandom.c
+new file mode 100644
+index 00000000..b2f4a07c
+--- /dev/null
++++ b/portability/getrandom.c
+@@ -0,0 +1,19 @@
++#include <sys/random.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <fcntl.h>
++
++ssize_t getrandom(void *buf, size_t buflen, unsigned int flags)
++{
++      int fd;
++      ssize_t ret;
++
++      fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
++      if (fd < 0)
++              return -1;
++
++      ret = read(fd, buf, buflen);
++      close(fd);
++      return ret;
++}
++
+diff --git a/portability/meson.build b/portability/meson.build
+index 89957c3c..3172044e 100644
+--- a/portability/meson.build
++++ b/portability/meson.build
+@@ -3,7 +3,8 @@ cc = meson.get_compiler('c')
+ libportability_src = []
+ check_symbols = [
+-      ['memrchr', 'memrchr.c', 'NEED_MEMRCHR', 'string.h'],
++      ['getrandom', 'getrandom.c', 'NEED_GETRANDOM', 'sys/random.h'],
++        ['memrchr', 'memrchr.c', 'NEED_MEMRCHR', 'string.h'],
+       ['mknodat', 'mknodat.c', 'NEED_MKNODAT', 'sys/stat.h'],
+       ['pipe2', 'pipe2.c', 'NEED_PIPE2', 'unistd.h'],
+       ['qsort_r', 'qsort_r.c', 'NEED_QSORT_R', 'stdlib.h'],
+diff --git a/portability/sys/random.h b/portability/sys/random.h
+new file mode 100644
+index 00000000..02d5b1ca
+--- /dev/null
++++ b/portability/sys/random.h
+@@ -0,0 +1,6 @@
++#include_next <sys/random.h>
++#include <sys/types.h>
++
++#ifdef NEED_GETRANDOM
++ssize_t getrandom(void *buf, size_t buflen, unsigned int flags);
++#endif
+diff --git a/src/apk_crypto.h b/src/apk_crypto.h
+index 7de88dfc..5cae3bfe 100644
+--- a/src/apk_crypto.h
++++ b/src/apk_crypto.h
+@@ -12,7 +12,12 @@
+ #include <string.h>
+ #include "apk_defines.h"
+ #include "apk_blob.h"
++
++#if defined(CRYPTO_USE_OPENSSL)
+ #include "apk_crypto_openssl.h"
++#elif defined(CRYPTO_USE_MBEDTLS)
++#include "apk_crypto_mbedtls.h"
++#endif
+ // Digest
+diff --git a/src/apk_crypto_mbedtls.h b/src/apk_crypto_mbedtls.h
+new file mode 100644
+index 00000000..5481d149
+--- /dev/null
++++ b/src/apk_crypto_mbedtls.h
+@@ -0,0 +1,30 @@
++/* apk_crypto_mbedtls.h - Alpine Package Keeper (APK)
++ *
++ * Copyright (C) 2024
++ * All rights reserved.
++ *
++ * SPDX-License-Identifier: GPL-2.0-only
++ */
++
++#ifndef APK_CRYPTO_MBEDTLS_H
++#define APK_CRYPTO_MBEDTLS_H
++
++#include <mbedtls/md.h>
++#include <mbedtls/pk.h>
++#include <mbedtls/bignum.h>
++
++struct apk_pkey {
++      uint8_t id[16];
++      mbedtls_pk_context key;
++};
++
++struct apk_digest_ctx {
++      mbedtls_md_context_t mdctx;
++      struct apk_pkey *sigver_key;
++      uint8_t alg;
++};
++
++/* based on mbedtls' internal pkwrite.h calculations */
++#define APK_ENC_KEY_MAX_LENGTH          (38 + 2 * MBEDTLS_MPI_MAX_SIZE)
++
++#endif
+diff --git a/src/crypto_mbedtls.c b/src/crypto_mbedtls.c
+new file mode 100644
+index 00000000..73d60e9d
+--- /dev/null
++++ b/src/crypto_mbedtls.c
+@@ -0,0 +1,285 @@
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <fcntl.h>
++#include <sys/random.h>
++#include <sys/stat.h>
++#include <unistd.h>
++
++#include <mbedtls/platform.h>
++#include <mbedtls/md.h>
++#include <mbedtls/pk.h>
++#include <mbedtls/entropy.h>
++
++#ifdef MBEDTLS_PSA_CRYPTO_C
++#include <psa/crypto.h>
++#endif
++
++#include "apk_crypto.h"
++
++static inline const mbedtls_md_type_t apk_digest_alg_to_mbedtls_type(uint8_t alg) {
++      switch (alg) {
++      case APK_DIGEST_NONE:   return MBEDTLS_MD_NONE;
++      case APK_DIGEST_MD5:    return MBEDTLS_MD_MD5;
++      case APK_DIGEST_SHA1:   return MBEDTLS_MD_SHA1;
++      case APK_DIGEST_SHA256_160:
++      case APK_DIGEST_SHA256: return MBEDTLS_MD_SHA256;
++      case APK_DIGEST_SHA512: return MBEDTLS_MD_SHA512;
++      default:
++              assert(alg);
++              return MBEDTLS_MD_NONE;
++      }
++}
++
++static inline const mbedtls_md_info_t *apk_digest_alg_to_mdinfo(uint8_t alg)
++{
++      return mbedtls_md_info_from_type(
++              apk_digest_alg_to_mbedtls_type(alg)
++      );
++}
++
++int apk_digest_calc(struct apk_digest *d, uint8_t alg, const void *ptr, size_t sz)
++{
++      if (mbedtls_md(apk_digest_alg_to_mdinfo(alg), ptr, sz, d->data))
++              return -APKE_CRYPTO_ERROR;
++
++      apk_digest_set(d, alg);
++      return 0;
++}
++
++int apk_digest_ctx_init(struct apk_digest_ctx *dctx, uint8_t alg)
++{
++      dctx->alg = alg;
++
++      mbedtls_md_init(&dctx->mdctx);
++      if (alg == APK_DIGEST_NONE) return 0;
++      if (mbedtls_md_setup(&dctx->mdctx, apk_digest_alg_to_mdinfo(alg), 0) ||
++              mbedtls_md_starts(&dctx->mdctx))
++              return -APKE_CRYPTO_ERROR;
++
++      return 0;
++}
++
++int apk_digest_ctx_reset(struct apk_digest_ctx *dctx)
++{
++      if (dctx->alg == APK_DIGEST_NONE) return 0;
++      if (mbedtls_md_starts(&dctx->mdctx)) return -APKE_CRYPTO_ERROR;
++      return 0;
++}
++
++int apk_digest_ctx_reset_alg(struct apk_digest_ctx *dctx, uint8_t alg)
++{
++      mbedtls_md_free(&dctx->mdctx);
++
++      dctx->alg = alg;
++      if (alg == APK_DIGEST_NONE) return 0;
++      if (mbedtls_md_setup(&dctx->mdctx, apk_digest_alg_to_mdinfo(alg), 0) ||
++              mbedtls_md_starts(&dctx->mdctx))
++              return -APKE_CRYPTO_ERROR;
++
++      return 0;
++}
++
++void apk_digest_ctx_free(struct apk_digest_ctx *dctx)
++{
++      mbedtls_md_free(&dctx->mdctx);
++}
++
++int apk_digest_ctx_update(struct apk_digest_ctx *dctx, const void *ptr, size_t sz)
++{
++      if (dctx->alg == APK_DIGEST_NONE) return 0;
++      return mbedtls_md_update(&dctx->mdctx, ptr, sz) == 0 ? 0 : -APKE_CRYPTO_ERROR;
++}
++
++int apk_digest_ctx_final(struct apk_digest_ctx *dctx, struct apk_digest *d)
++{
++      if (mbedtls_md_finish(&dctx->mdctx, d->data)) {
++              apk_digest_reset(d);
++              return -APKE_CRYPTO_ERROR;
++      }
++
++      d->alg = dctx->alg;
++      d->len = apk_digest_alg_len(d->alg);
++      return 0;
++}
++
++static int apk_load_file_at(int dirfd, const char *fn, unsigned char **buf, size_t *n)
++{
++      struct stat stats;
++      size_t size;
++      int fd;
++
++      if ((fd = openat(dirfd, fn, O_RDONLY|O_CLOEXEC)) < 0)
++              return -errno;
++
++      if (fstat(fd, &stats)) {
++              close(fd);
++              return -errno;
++      }
++
++      size = (size_t)stats.st_size;
++      *n = size;
++
++      if (size == 0 || (*buf = mbedtls_calloc(1, size + 1)) == NULL)
++              return MBEDTLS_ERR_PK_ALLOC_FAILED;
++
++      if (read(fd, *buf, size) != size) {
++              close(fd);
++
++              mbedtls_platform_zeroize(*buf, size);
++              mbedtls_free(*buf);
++
++              return MBEDTLS_ERR_PK_FILE_IO_ERROR;
++      }
++      close(fd);
++
++      (*buf)[size] = '\0';
++
++      if (strstr((const char *) *buf, "-----BEGIN ") != NULL) {
++              ++*n;
++      }
++
++      return 0;
++}
++
++static int apk_pkey_init(struct apk_pkey *pkey)
++{
++      unsigned char dig[APK_DIGEST_MAX_LENGTH];
++      unsigned char pub[APK_ENC_KEY_MAX_LENGTH] = {};
++      unsigned char *c;
++      int len, r = -APKE_CRYPTO_ERROR;
++
++      c = pub + APK_ENC_KEY_MAX_LENGTH;
++
++      // key is written backwards into pub starting at c!
++      if ((len = mbedtls_pk_write_pubkey(&c, pub, &pkey->key)) < 0) return -APKE_CRYPTO_ERROR;
++      if (!mbedtls_md(apk_digest_alg_to_mdinfo(APK_DIGEST_SHA512), c, len, dig)) {
++              memcpy(pkey->id, dig, sizeof pkey->id);
++              r = 0;
++      }
++
++      return r;
++}
++
++void apk_pkey_free(struct apk_pkey *pkey)
++{
++      mbedtls_pk_free(&pkey->key);
++}
++
++static int apk_random(void *ctx, unsigned char *out, size_t len)
++{
++      return (int)getrandom(out, len, 0);
++}
++
++#if MBEDTLS_VERSION_NUMBER >= 0x03000000
++static inline int apk_mbedtls_parse_privkey(struct apk_pkey *pkey, const unsigned char *buf, size_t blen)
++{
++      return mbedtls_pk_parse_key(&pkey->key, buf, blen, NULL, 0, apk_random, NULL);
++}
++static inline int apk_mbedtls_sign(struct apk_digest_ctx *dctx, struct apk_digest *dig,
++                                 unsigned char *sig, size_t *sig_len)
++{
++      return mbedtls_pk_sign(&dctx->sigver_key->key, apk_digest_alg_to_mbedtls_type(dctx->alg),
++                             (const unsigned char *)&dig->data, dig->len, sig, sizeof *sig, sig_len,
++                             apk_random, NULL);
++}
++#else
++static inline int apk_mbedtls_parse_privkey(struct apk_pkey *pkey, const unsigned char *buf, size_t blen)
++{
++      return mbedtls_pk_parse_key(&pkey->key, buf, blen, NULL, 0);
++}
++static inline int apk_mbedtls_sign(struct apk_digest_ctx *dctx, struct apk_digest *dig,
++                                 unsigned char *sig, size_t *sig_len)
++{
++      return mbedtls_pk_sign(&dctx->sigver_key->key, apk_digest_alg_to_mbedtls_type(dctx->alg),
++                             (const unsigned char *)&dig->data, dig->len, sig, sig_len, apk_random, NULL);
++}
++#endif
++
++int apk_pkey_load(struct apk_pkey *pkey, int dirfd, const char *fn)
++{
++      unsigned char *buf = NULL;
++      size_t blen = 0;
++      int ret;
++
++      if (apk_load_file_at(dirfd, fn, &buf, &blen))
++              return -APKE_CRYPTO_ERROR;
++
++      mbedtls_pk_init(&pkey->key);
++      if ((ret = mbedtls_pk_parse_public_key(&pkey->key, buf, blen)) != 0)
++              ret = apk_mbedtls_parse_privkey(pkey, buf, blen);
++
++      mbedtls_platform_zeroize(buf, blen);
++      mbedtls_free(buf);
++      if (ret != 0)
++              return -APKE_CRYPTO_KEY_FORMAT;
++
++      return apk_pkey_init(pkey);
++}
++
++int apk_sign_start(struct apk_digest_ctx *dctx, uint8_t alg, struct apk_pkey *pkey)
++{
++      if (apk_digest_ctx_reset_alg(dctx, alg))
++              return -APKE_CRYPTO_ERROR;
++
++      dctx->sigver_key = pkey;
++
++      return 0;
++}
++
++int apk_sign(struct apk_digest_ctx *dctx, void *sig, size_t *len)
++{
++      struct apk_digest dig;
++      int r = 0;
++
++      if (apk_digest_ctx_final(dctx, &dig))
++              return -APKE_SIGNATURE_GEN_FAILURE;
++
++      if (apk_mbedtls_sign(dctx, &dig, sig, len))
++              r = -APKE_SIGNATURE_GEN_FAILURE;
++
++      dctx->sigver_key = NULL;
++      return r;
++}
++
++int apk_verify_start(struct apk_digest_ctx *dctx, uint8_t alg, struct apk_pkey *pkey)
++{
++      if (apk_digest_ctx_reset_alg(dctx, alg))
++              return -APKE_CRYPTO_ERROR;
++
++      dctx->sigver_key = pkey;
++
++      return 0;
++}
++
++int apk_verify(struct apk_digest_ctx *dctx, void *sig, size_t len)
++{
++      struct apk_digest dig;
++      int r = 0;
++
++      if (apk_digest_ctx_final(dctx, &dig))
++              return -APKE_SIGNATURE_GEN_FAILURE;
++
++      if (mbedtls_pk_verify(&dctx->sigver_key->key, apk_digest_alg_to_mbedtls_type(dctx->alg),
++                            (const unsigned char *)&dig.data, dig.len, sig, len))
++              r = -APKE_SIGNATURE_INVALID;
++
++      dctx->sigver_key = NULL;
++      return r;
++}
++
++static void apk_crypto_cleanup(void)
++{
++#ifdef MBEDTLS_PSA_CRYPTO_C
++      mbedtls_psa_crypto_free();
++#endif
++}
++
++void apk_crypto_init(void)
++{
++      atexit(apk_crypto_cleanup);
++      
++#ifdef MBEDTLS_PSA_CRYPTO_C
++      psa_crypto_init();
++#endif
++}
+diff --git a/src/meson.build b/src/meson.build
+index 38e9d3b0..e1204fc0 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -1,3 +1,4 @@
++crypto_backend = get_option('crypto_backend')
+ url_backend = get_option('url_backend')
+ libapk_so_version = '2.99.0'
+@@ -15,7 +16,7 @@ libapk_src = [
+       'common.c',
+       'context.c',
+       'crypto.c',
+-      'crypto_openssl.c',
++        'crypto_@0@.c'.format(crypto_backend),
+       'ctype.c',
+       'database.c',
+       'extract_v2.c',
+@@ -40,7 +41,7 @@ libapk_headers = [
+       'apk_atom.h',
+       'apk_blob.h',
+       'apk_crypto.h',
+-      'apk_crypto_openssl.h',
++        'apk_crypto_@0@.h'.format(crypto_backend),
+       'apk_ctype.h',
+       'apk_database.h',
+       'apk_defines.h',
+@@ -89,6 +90,17 @@ apk_src = [
+       'applet.c',
+ ]
++apk_cargs = [
++      '-DAPK_VERSION="' + meson.project_version() + '"',
++      '-D_ATFILE_SOURCE',
++]
++
++if crypto_backend == 'openssl'
++      apk_cargs += [ '-DCRYPTO_USE_OPENSSL' ]
++elif crypto_backend == 'mbedtls'
++      apk_cargs += [ '-DCRYPTO_USE_MBEDTLS' ]
++endif
++
+ if lua_bin.found()
+       genhelp_script = files('genhelp.lua')
+       genhelp_args = [lua_bin, genhelp_script, '@INPUT@']
+@@ -115,11 +127,6 @@ endif
+ apk_src += [ generated_help ]
+-apk_cargs = [
+-      '-DAPK_VERSION="' + meson.project_version() + '"',
+-      '-D_ATFILE_SOURCE',
+-]
+-
+ apk_arch_prefix = get_option('arch_prefix')
+ if apk_arch_prefix != ''
+       apk_cargs += ['-DAPK_ARCH_PREFIX="@0@"'.format(apk_arch_prefix)]
+-- 
+GitLab
+
+
+From 34bb1021284dccbf97f02b0a0bb9e751b8887cad Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 16 Apr 2024 17:56:45 +0200
+Subject: [PATCH 4/4] add option to configure crypto backend in legacy make
+ build system
+
+Define CRYPTO to select mbedtls as alternative crypto backend. By
+default openssl is used.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ src/Makefile | 20 +++++++++++++++-----
+ 1 file changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/src/Makefile b/src/Makefile
+index efdc68df..97db0e72 100644
+--- a/src/Makefile
++++ b/src/Makefile
+@@ -20,9 +20,9 @@ libapk_soname                := 2.99.0
+ libapk_so             := $(obj)/libapk.so.$(libapk_soname)
+ libapk.so.$(libapk_soname)-objs := \
+       adb.o adb_comp.o adb_walk_adb.o adb_walk_genadb.o adb_walk_gentext.o adb_walk_text.o apk_adb.o \
+-      atom.o blob.o commit.o common.o context.o crypto.o crypto_openssl.o ctype.o database.o hash.o \
+-      extract_v2.o extract_v3.o fs_fsys.o fs_uvol.o io.o io_gunzip.o tar.o package.o pathbuilder.o \
+-      print.o solver.o trust.o version.o
++      atom.o blob.o commit.o common.o context.o crypto.o ctype.o database.o hash.o extract_v2.o \
++      extract_v3.o fs_fsys.o fs_uvol.o io.o io_gunzip.o tar.o package.o pathbuilder.o print.o \
++      solver.o trust.o version.o
+ libapk.so.$(libapk_soname)-libs :=
+@@ -34,6 +34,16 @@ libapk.so.$(libapk_soname)-objs += io_url_libfetch.o
+ libapk.so.$(libapk_soname)-libs += libfetch/libfetch.a
+ endif
++ifeq ($(CRYPTO),mbedtls)
++CRYPTO_CFLAGS         := $(shell $(PKG_CONFIG) --cflags mbedtls mbedcrypto) -DCRYPTO_USE_MBEDTLS
++CRYPTO_LIBS           := $(shell $(PKG_CONFIG) --libs mbedtls mbedcrypto)
++libapk.so.$(libapk_soname)-objs += crypto_mbedtls.o
++else
++CRYPTO_CFLAGS         := $(shell $(PKG_CONFIG) --cflags openssl) -DCRYPTO_USE_OPENSSL
++CRYPTO_LIBS           := $(shell $(PKG_CONFIG) --libs openssl)
++libapk.so.$(libapk_soname)-objs += crypto_openssl.o
++endif
++
+ # ZSTD support can be disabled
+ ifneq ($(ZSTD),no)
+ ZSTD_CFLAGS           := $(shell $(PKG_CONFIG) --cflags libzstd)
+@@ -100,9 +110,9 @@ LIBS_apk.static            := -Wl,--as-needed -ldl -Wl,--no-as-needed
+ LDFLAGS_apk           += -L$(obj)
+ LDFLAGS_apk-test      += -L$(obj)
+-CFLAGS_ALL            += $(OPENSSL_CFLAGS) $(ZLIB_CFLAGS) $(ZSTD_CFLAGS)
++CFLAGS_ALL            += $(CRYPTO_CFLAGS) $(ZLIB_CFLAGS) $(ZSTD_CFLAGS)
+ LIBS                  := -Wl,--as-needed \
+-                              $(OPENSSL_LIBS) $(ZLIB_LIBS) $(ZSTD_LIBS) \
++                              $(CRYPTO_LIBS) $(ZLIB_LIBS) $(ZSTD_LIBS) \
+                          -Wl,--no-as-needed
+ # Help generation
+-- 
+GitLab