# syntax=docker/dockerfile:1
#
# CI base image for LibJWT: debian:forky with every backend usable.
#
#   * OpenSSL          - from forky apt (3.6.x, >= 3.5 so the OpenSSL ML-DSA path works)
#   * GnuTLS           - built FROM SOURCE --with-leancrypto so the ML-DSA
#                        (FIPS 204 / RFC 9964, kty="AKP") SUCCESS path works
#   * leancrypto       - built FROM SOURCE; the PQC provider GnuTLS links against
#   * MbedTLS          - built FROM SOURCE (3.6.x LTS, the tested baseline; the
#                        PSA-based backend also builds against MbedTLS 4.x)
#   * jansson + json-c - both JSON backends, from apt
#   * libcurl, check, bats, jq, lcov, valgrind - from apt
#
# Only the three components that genuinely need a custom/newer build are built
# from source; every build-dependency comes from native Debian apt.
#
# We deliberately do NOT install forky's libgnutls28-dev or libmbedtls-dev:
# their gnutls.pc / mbedcrypto.pc under /usr/lib/<triplet>/pkgconfig would
# shadow our from-source .pc files and make CMake link the apt (no-leancrypto)
# libraries instead of ours. forky's apt GnuTLS is *also* 3.8.13, so the only
# reliable discriminator is the install prefix (/usr/local) + ML-DSA capability.
#
# Published at: ghcr.io/benmcollins/libjwt/gnutls-leancrypto-mbedtls
# Rebuilt by:   .github/workflows/ci-image.yml
#
FROM debian:forky

LABEL org.opencontainers.image.source="https://github.com/benmcollins/libjwt"
LABEL org.opencontainers.image.description="LibJWT CI base: debian:forky + OpenSSL (apt), GnuTLS built --with-leancrypto (ML-DSA success path), MbedTLS 3.6.x LTS from source, jansson + json-c, libcurl, check, bats, jq, lcov, valgrind."
LABEL org.opencontainers.image.licenses="MPL-2.0"

# ---- Pinned from-source versions (confirmed latest as of 2026-06-16) --------
ARG LEANCRYPTO_VERSION=v1.7.2
ARG GNUTLS_VERSION=3.8.13
ARG MBEDTLS_VERSION=3.6.6
# GnuTLS tarballs are laid out by major.minor; keep this in sync with the
# major.minor of GNUTLS_VERSION when bumping.
ARG GNUTLS_SERIES=v3.8

ENV DEBIAN_FRONTEND=noninteractive \
    LC_ALL=C.UTF-8 \
    LANG=C.UTF-8

# All three from-source installs use --libdir=lib, so their .pc files land in
# /usr/local/lib/pkgconfig and their .so files in /usr/local/lib (NOT Debian's
# multiarch lib/<triplet>). That keeps discovery arch-agnostic: this same
# Dockerfile builds correctly on amd64 and arm64. The apt deps' .pc files live
# on pkg-config's default system search path and need no help here.
ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig

# ---------------------------------------------------------------------------
# 1. apt dependencies: libjwt's own build/test deps + everything needed to
#    compile leancrypto, GnuTLS, and MbedTLS from source (all build-deps from
#    native Debian). NOTE: libgnutls28-dev and libmbedtls-dev are intentionally
#    absent so their apt .pc files cannot shadow our from-source ones.
# ---------------------------------------------------------------------------
RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends \
        ca-certificates git curl xz-utils \
        build-essential cmake pkg-config \
        gettext autoconf automake libtool \
        meson ninja-build nasm \
        python3 python3-jinja2 python3-jsonschema perl \
        nettle-dev libgmp-dev libtasn1-6-dev libidn2-dev libunistring-dev zlib1g-dev \
        libssl-dev libjansson-dev libjson-c-dev libcurl4-openssl-dev \
        check bats jq lcov valgrind; \
    rm -rf /var/lib/apt/lists/*

# Search /usr/local/lib first at run time so our from-source GnuTLS/MbedTLS/
# leancrypto win over any apt-provided copy.
RUN set -eux; \
    echo '/usr/local/lib' > /etc/ld.so.conf.d/000-usrlocal.conf; \
    ldconfig

# ---------------------------------------------------------------------------
# 2. leancrypto (PQC provider) -> /usr/local
#    -Ddefault_library=both: GnuTLS STATIC-links the provider (no dlopen), so a
#    static archive must exist; the shared lib is kept for completeness.
#    --libdir=lib puts leancrypto.pc on the default pkg-config path.
#    -Dtests=disabled skips the (large) test-binary suite we don't need; the
#    algorithm self-tests (enable_selftests, default on) stay enabled.
# ---------------------------------------------------------------------------
RUN set -eux; \
    git clone --depth 1 --branch "${LEANCRYPTO_VERSION}" \
        https://github.com/smuellerDD/leancrypto.git /tmp/leancrypto; \
    meson setup /tmp/leancrypto/build /tmp/leancrypto \
        --prefix=/usr/local --libdir=lib --buildtype=release \
        -Ddefault_library=both -Dtests=disabled; \
    meson compile -C /tmp/leancrypto/build; \
    meson install -C /tmp/leancrypto/build; \
    ldconfig; \
    pkg-config --exists leancrypto; \
    echo "leancrypto $(pkg-config --modversion leancrypto) installed"; \
    rm -rf /tmp/leancrypto

# ---------------------------------------------------------------------------
# 3. GnuTLS --with-leancrypto -> /usr/local
#    leancrypto.pc MUST be discoverable now (it is, via PKG_CONFIG_PATH), or
#    ./configure aborts in PKG_CHECK_MODULES([LEANCRYPTO],[leancrypto >= 1.2.0]).
#    Tools (certtool/gnutls-cli) are kept on purpose: certtool is the ML-DSA
#    smoke test below and some BATS tests may shell out to them. We use the apt
#    libtasn1 / libunistring (native build-deps), not GnuTLS's bundled copies.
# ---------------------------------------------------------------------------
RUN set -eux; \
    cd /tmp; \
    curl -fLO "https://www.gnupg.org/ftp/gcrypt/gnutls/${GNUTLS_SERIES}/gnutls-${GNUTLS_VERSION}.tar.xz"; \
    tar xf "gnutls-${GNUTLS_VERSION}.tar.xz"; \
    cd "gnutls-${GNUTLS_VERSION}"; \
    ./configure \
        --prefix=/usr/local \
        --with-leancrypto \
        --without-p11-kit \
        --without-tpm2 \
        --disable-doc \
        --disable-tests \
        --disable-guile \
        --disable-libdane \
        --enable-shared \
        --enable-static; \
    make -j"$(nproc)"; \
    make install; \
    ldconfig; \
    rm -rf "/tmp/gnutls-${GNUTLS_VERSION}" "/tmp/gnutls-${GNUTLS_VERSION}.tar.xz"

# ---------------------------------------------------------------------------
# 4. MbedTLS 3.6.x LTS -> /usr/local
#    Use the NAMED release asset (.tar.bz2) - it is self-contained (no
#    framework/ submodule). The default config is sufficient for libjwt's
#    JWS/JWK/JWE, so do NOT set MBEDTLS_CONFIG_FILE. The stock build installs
#    mbedcrypto.pc (+ mbedtls.pc, mbedx509.pc) to lib/pkgconfig.
# ---------------------------------------------------------------------------
RUN set -eux; \
    cd /tmp; \
    curl -fsSL -o "mbedtls-${MBEDTLS_VERSION}.tar.bz2" \
        "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2"; \
    tar xjf "mbedtls-${MBEDTLS_VERSION}.tar.bz2"; \
    cd "mbedtls-${MBEDTLS_VERSION}"; \
    cmake -S . -B build \
        -DCMAKE_BUILD_TYPE=Release \
        -DUSE_SHARED_MBEDTLS_LIBRARY=On \
        -DUSE_STATIC_MBEDTLS_LIBRARY=Off \
        -DENABLE_TESTING=Off \
        -DENABLE_PROGRAMS=Off \
        -DCMAKE_INSTALL_PREFIX=/usr/local \
        -DCMAKE_INSTALL_LIBDIR=lib \
        -DMBEDTLS_FATAL_WARNINGS=Off; \
    cmake --build build -j"$(nproc)"; \
    cmake --install build; \
    ldconfig; \
    rm -rf "/tmp/mbedtls-${MBEDTLS_VERSION}" "/tmp/mbedtls-${MBEDTLS_VERSION}.tar.bz2"

# ---------------------------------------------------------------------------
# 5. Build-time verification - fail the image build if any guarantee is broken.
# ---------------------------------------------------------------------------
RUN set -eux; \
    # 5a. pkg-config resolves the FROM-SOURCE installs (prefix == /usr/local), \
    #     not an apt copy (forky's apt GnuTLS reports the same 3.8.13 version). \
    test "$(pkg-config --variable=prefix gnutls)" = "/usr/local"; \
    test "$(pkg-config --variable=prefix mbedcrypto)" = "/usr/local"; \
    # 5b. Versions satisfy libjwt's pkg_check_modules floors (and ML-DSA gates). \
    pkg-config --atleast-version=3.8.10 gnutls; \
    pkg-config --atleast-version=3.6.0  mbedcrypto; \
    pkg-config --atleast-version=1.2.0  leancrypto; \
    pkg-config --atleast-version=3.5.0  openssl; \
    echo "openssl    $(pkg-config --modversion openssl)"; \
    echo "gnutls     $(pkg-config --modversion gnutls)"; \
    echo "mbedcrypto $(pkg-config --modversion mbedcrypto)"; \
    echo "leancrypto $(pkg-config --modversion leancrypto)"; \
    # 5c. GnuTLS's static link line resolves its private deps (leancrypto, \
    #     nettle, ...) without error. \
    pkg-config --static --libs gnutls >/dev/null; \
    # 5d. Runtime linker resolves our /usr/local GnuTLS, not the apt one. \
    ldd /usr/local/bin/gnutls-cli | grep -E 'libgnutls\.so' | grep -q '/usr/local/lib'; \
    # 5e. THE decisive check: GnuTLS actually has a PQC provider (leancrypto). \
    #     A stock GnuTLS with no provider fails ML-DSA key-gen with error -106. \
    #     The token is the non-hyphenated 'mldsa87' (certtool figure_key_type). \
    /usr/local/bin/certtool --generate-privkey \
        --key-type=mldsa87 --outfile=/tmp/mldsa87.key; \
    test -s /tmp/mldsa87.key; \
    rm -f /tmp/mldsa87.key; \
    echo "OK: GnuTLS ML-DSA / leancrypto provider verified"

# Sensible default working directory for CI checkouts.
WORKDIR /src
CMD ["/bin/bash"]
