enprot 0.5.57

Engyon Protected Text (EPT) — confidentiality processor and capability ledger
$BOTAN_MODULES = "$($(get-content 'ci\botan-modules') -join ',')"
$ErrorActionPreference = "Stop"

# setup msvc compiler environment
$vswhere = "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$vspath = & "$vswhere" -latest -property installationPath
Import-Module "$vspath\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath "$vspath" -DevCmdArguments '-arch=x64 -no_logo' -SkipAutomaticLocation

if (-not $Env:PREFIX) {
    $Env:PREFIX = (Join-Path $PWD 'botan-install').Replace('\', '/')
}
$WORKDIR = $PWD

# Cached-install skip: when the deploy job restores a warm PREFIX
# cache (actions/cache on installs/, keyed on this script + Botan
# version), the entire C stack below is already in place — skip
# ~30 min of builds. The epilogue (RNP_* / RUSTFLAGS exports,
# sanity checks) still runs unconditionally.
$stackCacheHit = (Test-Path (Join-Path $Env:PREFIX "lib/botan-3.lib")) -and `
                 (Test-Path (Join-Path $Env:PREFIX "lib/rnp.lib"))
if (-not $stackCacheHit) {

  # -------------------- 0. BZip2 (librnp find_package REQUIRED) --------------------
  # librnp's src/lib/CMakeLists.txt calls find_package(BZip2 REQUIRED)
  # unconditionally — even with ENABLE_BZIP2=OFF. Chocolatey's bzip2
  # only ships the exe + DLL, not a .lib. Build from source instead.
  $bzip2Ver = "1.0.8"
  & curl -fsSL "https://github.com/libarchive/bzip2/archive/refs/tags/bzip2-$bzip2Ver.tar.gz" -o bzip2.tar.gz
  tar -xzf bzip2.tar.gz
  $bzip2Dir = Get-ChildItem -Directory -Filter "bzip2-*" | Select-Object -First 1
  Push-Location -LiteralPath $bzip2Dir.FullName
  # bzip2's Makefile is Unix-oriented. For MSVC we compile the single
  # source file that produces the library.
  & cl /O2 /MT /c blocksort.c huffman.c crctable.c randtable.c compress.c decompress.c bzlib.c
  & lib /OUT:bzip2.lib blocksort.obj huffman.obj crctable.obj randtable.obj compress.obj decompress.obj bzlib.obj
  # Install headers + lib into PREFIX
  New-Item -ItemType Directory -Force -Path "$Env:PREFIX/include" | Out-Null
  New-Item -ItemType Directory -Force -Path "$Env:PREFIX/lib" | Out-Null
  Copy-Item bzlib.h "$Env:PREFIX/include/"
  Copy-Item bzip2.lib "$Env:PREFIX/lib/"
  $Env:BZIP2_INCLUDE_DIR = "$Env:PREFIX/include"
  $Env:BZIP2_LIBRARY = "$Env:PREFIX/lib/bzip2.lib"
  Pop-Location

  # -------------------- 0b. ZLIB (librnp find_package REQUIRED) --------------------
  $zlibVer = "1.3.1"
  & curl -fsSL "https://github.com/madler/zlib/releases/download/v$zlibVer/zlib-$zlibVer.tar.gz" -o zlib.tar.gz
  tar -xzf zlib.tar.gz
  $zlibDir = Get-ChildItem -Directory -Filter "zlib-*" | Select-Object -First 1
  Push-Location -LiteralPath $zlibDir.FullName
  # zlib has a CMake build — use it for MSVC compatibility. STATIC
  # build: CMake's default is BUILD_SHARED_LIBS=ON which produces
  # zlib1.dll + zlib.lib (import lib). The resulting binary would
  # fail at runtime with STATUS_DLL_NOT_FOUND. Force static so the
  # code is embedded in the final binary.
  # zlib's CMake names the static target `zlibstatic` → output
  # `zlibstatic.lib`. Rename to `zlib.lib` after install so both
  # rnp's find_package(ZLIB) and enprot's `static=zlib` link
  # directive resolve to the same file.
  New-Item -ItemType Directory -Force -Path build | Out-Null
  Push-Location build
  & cmake .. `
      -DCMAKE_INSTALL_PREFIX="$Env:PREFIX" `
      -DCMAKE_BUILD_TYPE=Release `
      -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded `
      -DBUILD_SHARED_LIBS=OFF
  & cmake --build . --config Release --parallel $Env:NUMBER_OF_PROCESSORS
  & cmake --install . --config Release
  Pop-Location
  Pop-Location
  $staticZlib = Join-Path $Env:PREFIX "lib/zlibstatic.lib"
  $targetZlib = Join-Path $Env:PREFIX "lib/zlib.lib"
  if (Test-Path $staticZlib) {
      Copy-Item $staticZlib $targetZlib -Force
  }
  $Env:ZLIB_ROOT = "$Env:PREFIX"

  # -------------------- 1. Botan (static, MT runtime) --------------------
  & git clone --depth 1 --branch "$Env:BOTAN_VERSION" https://github.com/randombit/botan
  Push-Location -LiteralPath botan
  & python .\configure.py --prefix="$Env:PREFIX" --without-documentation `
    --build-targets=static --minimized-build `
    --enable-modules="$BOTAN_MODULES" --msvc-runtime=MT `
    --cc=msvc --os=windows
  &{
    $ErrorActionPreference = 'Continue'
    nmake install
  }
  Pop-Location

  # -------------------- 2. JSON-C (librnp hard dep) --------------------
  & git clone --depth 1 --branch json-c-0.17-20230812 https://github.com/json-c/json-c.git "$WORKDIR\json-c-src"
  New-Item -ItemType Directory -Force -Path "$WORKDIR\json-c-src\build" | Out-Null
  Push-Location -LiteralPath "$WORKDIR\json-c-src\build"
  # Botan headers default BOTAN_DLL to __declspec(dllimport) on MSVC;
  # consuming the STATIC botan-3.lib that way imports __imp_botan_*
  # thunks that don't exist (LNK2019). Empty the macro, as every
  # static Botan consumer must. (Comments cannot sit inside a
  # backtick-continued expression — PowerShell breaks the continuation.)
  & cmake .. `
      -DCMAKE_BUILD_TYPE=Release `
      -DBUILD_SHARED_LIBS=OFF `
      -DBUILD_TESTING=OFF `
      -DCMAKE_INSTALL_PREFIX="$Env:PREFIX" `
      -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded
  & cmake --build . --config Release --parallel $Env:NUMBER_OF_PROCESSORS
  & cmake --install . --config Release
  Pop-Location

  # -------------------- 3. librnp --------------------
  $RNP_VERSION = "0.18.1"
  & git clone --branch "v$RNP_VERSION" --depth 1 https://github.com/rnpgp/rnp.git "$WORKDIR\rnp-src"
  Push-Location -LiteralPath "$WORKDIR\rnp-src"
  & git submodule update --init --recursive
  Pop-Location

  New-Item -ItemType Directory -Force -Path "$WORKDIR\rnp-src\build" | Out-Null
  Push-Location -LiteralPath "$WORKDIR\rnp-src\build"

  # Point librnp's CMake at the just-built botan + json-c. Disable the
  # features that need botan modules we don't ship (PQC needs DILITHIUM,
  # KYBER; crypto-refresh needs HKDF which is enabled but they also want
  # the broader refresh surface).
  # Point librnp's CMake at the just-built botan + json-c. Provide
  # stub dirent.h + getopt.h — rnp's CMake find_path() calls these
  # REQUIRED for the CLI tool (src/rnp/), but we only need the library
  # (librnp) for rnp-rs. The stubs satisfy find_path without adding
  # real POSIX compat code.
  # Stub POSIX headers — rnp's LIBRARY code (not just CLI) uses
  # opendir/readdir/S_ISDIR internally for key-store directory listing.
  # MSVC doesn't ship these. Use tronkko/dirent — a well-known Windows
  # port of POSIX dirent.h that provides the full API.
  & curl -fsSL "https://raw.githubusercontent.com/tronkko/dirent/1.24/include/dirent.h" -o "$Env:PREFIX/include/dirent.h"
  Set-Content -Path "$Env:PREFIX/include/getopt.h" -Value "/* stub — not used by librnp library */"
  # S_ISDIR macro — MSVC's <sys/stat.h> doesn't define it. Add it
  # to dirent.h (already there in tronkko/dirent, but double-check).
  # getopt stub lib — CMake find_library needs an actual .lib file.
  Set-Content -Path "$Env:PREFIX/include/getopt_stub.c" -Value "int __enprot_getopt_stub(void) { return 0; }"
  Push-Location "$Env:PREFIX/include"
  & cl /c /MT getopt_stub.c 2>&1 | Out-Null
  & lib /OUT:"$Env:PREFIX/lib/getopt.lib" getopt_stub.obj 2>&1 | Out-Null
  Pop-Location

  & cmake .. `
      -DCMAKE_BUILD_TYPE=Release `
      -DBUILD_SHARED_LIBS=OFF `
      -DBUILD_TESTING=OFF `
      -DENABLE_DOC=OFF `
      -DCRYPTO_BACKEND=botan `
      -DENABLE_PQC=OFF `
      -DENABLE_CRYPTO_REFRESH=OFF `
      -DENABLE_COMPRESSION=OFF `
      -DENABLE_BZIP2=OFF `
      -DENABLE_ZLIB=OFF `
      -DCMAKE_INSTALL_PREFIX="$Env:PREFIX" `
      -DBOTAN_INCLUDE_DIR="$Env:PREFIX/include/botan-3" `
      -DBOTAN_LIBRARY="$Env:PREFIX/lib/botan-3.lib" `
      -DJSON-C_INCLUDE_DIR="$Env:PREFIX/include/json-c" `
      -DJSON-C_LIBRARY="$Env:PREFIX/lib/json-c.lib" `
      -DDIRENT_INCLUDE_DIR="$Env:PREFIX/include" `
      -DGETOPT_INCLUDE_DIR="$Env:PREFIX/include" `
      -DGETOPT_LIBRARY="$Env:PREFIX/lib/getopt.lib" `
      -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded `
      -DCMAKE_C_FLAGS="/DBOTAN_DLL=" `
      -DCMAKE_CXX_FLAGS="/DBOTAN_DLL="
  if ($LASTEXITCODE -ne 0) { throw "librnp cmake configure failed" }

  # Build ONLY the librnp target, not the CLI tools (rnp/rnpkeys).
  # The CLI needs dirent.h/getopt.h (stubs above satisfy find_path
  # but not actual compilation of CLI source).
  & cmake --build . --config Release --target librnp --parallel $Env:NUMBER_OF_PROCESSORS
  if ($LASTEXITCODE -ne 0) { throw "librnp cmake build failed" }

  # cmake --install installs headers + library correctly, then FAILS
  # when trying to install the unbuilt rnp.exe CLI. That error is
  # expected — we only built the librnp target. Tolerate it: the
  # headers and .lib are already in place by the time the error fires.
  $ErrorActionPreference = 'SilentlyContinue'
  & cmake --install . --config Release 2>$null
  $ErrorActionPreference = 'Stop'
  $Error.Clear()
  $global:LASTEXITCODE = 0

  Pop-Location

}

# -------------------- 4. Tell rnp-rs where to find it --------------------
# rnp-rs's build.rs reads RNP_INCLUDE_DIR / RNP_LIB_DIR if set;
# otherwise falls back to /usr/include (Linux/macOS only).
$Env:RNP_INCLUDE_DIR = "$Env:PREFIX/include"
$Env:RNP_LIB_DIR     = "$Env:PREFIX/lib"
echo "RNP_INCLUDE_DIR=$Env:RNP_INCLUDE_DIR" | Out-File -Append -Encoding ascii $Env:GITHUB_ENV
echo "RNP_LIB_DIR=$Env:RNP_LIB_DIR"         | Out-File -Append -Encoding ascii $Env:GITHUB_ENV

# Make the just-built static libs findable globally. enprot's build.rs
# emits `cargo:rustc-link-search=native=$PREFIX/lib`, but that only
# applies to enprot's own compilation — not to dependency build scripts
# like botan-sys, which independently emit `cargo:rustc-link-lib=static=
# botan-3` and need to resolve the .lib at compile time. RUSTFLAGS env
# var is the global mechanism: it applies to every rustc invocation,
# including dep compilation.
# Note: RUSTFLAGS takes precedence over .cargo/config.toml [build]
# rustflags, so we carry the remap-path-prefix forward here too.
$Env:RUSTFLAGS = "--remap-path-prefix=/usr/local/cargo=/cargo -L native=$Env:PREFIX/lib"
echo "RUSTFLAGS=$Env:RUSTFLAGS" | Out-File -Append -Encoding ascii $Env:GITHUB_ENV

# PREFIX is available to cargo build.
echo "PREFIX=$Env:PREFIX" | Out-File -Append -Encoding ascii $Env:GITHUB_ENV

# Sanity check: rnp.h must be present.
$Expected = Join-Path $Env:PREFIX "include/rnp/rnp.h"
if (-not (Test-Path $Expected)) {
    throw "librnp header missing at $Expected"
}
Write-Host "==> Botan + json-c + librnp built and installed to $Env:PREFIX"