From 6424305640533441b975dc18231b9dd0812a2dbf Mon Sep 17 00:00:00 2001 From: Simon Dick Date: Tue, 28 Jul 2026 05:31:25 +0100 Subject: [PATCH] Add AmiSSL-vs-builtin and asm-vs-C on-target benchmarks (#85 groundwork) Dev-only Copperline benchmark tooling, wired into make amissl-bench / make asm-bench: - amissl-bench.sh compares AmiSSL's PKCS5_PBKDF2_HMAC_SHA1 against the builtin pbkdf2_hmac_sha1, same boot/CPU pass. Boots from a copy-on-write clone of the project's Workbench 3.2 test install rather than a synthetic minimal disk - OpenAmiSSLTags() reliably crashed ramlib on a from-scratch boot volume that only had the libraries copied into LIBS: (also needs the `AmiSSL:` assign that S:User-Startup sets up, evidently for something InitAmiSSL() reads even with no networking). - asm-bench.sh compares the portable C SHA-1 compress function against the hand-written 68000 asm one (#47), same boot/CPU, real 68000. - fetch-amissl-sdk.sh fetches + verifies the pinned AmiSSL SDK (Developer headers + libamisslstubs.a) needed to link amissl-bench. Findings posted to #85: AmiSSL is 12.7% slower than builtin on 68020, but 15-17% faster on 68030/68040 - the earlier "not worth it" read didn't generalize across CPU tiers. Co-Authored-By: Claude Sonnet 5 --- Makefile | 14 ++- tests/copperline/amissl-bench.sh | 111 ++++++++++++++++++++++++ tests/copperline/amisslbench.c | 124 +++++++++++++++++++++++++++ tests/copperline/asm-bench.sh | 80 +++++++++++++++++ tests/copperline/asmbench.c | 90 +++++++++++++++++++ tests/copperline/fetch-amissl-sdk.sh | 120 ++++++++++++++++++++++++++ 6 files changed, 538 insertions(+), 1 deletion(-) create mode 100755 tests/copperline/amissl-bench.sh create mode 100644 tests/copperline/amisslbench.c create mode 100755 tests/copperline/asm-bench.sh create mode 100644 tests/copperline/asmbench.c create mode 100755 tests/copperline/fetch-amissl-sdk.sh diff --git a/Makefile b/Makefile index e886b5a..03b9cea 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ BUILD := build QUIRC_HOST_OBJS := $(patsubst src/qr/%.c,$(BUILD)/qr-host/%.o,$(QUIRC_SRCS)) QUIRC_M68K_OBJS := $(patsubst src/qr/%.c,$(BUILD)/qr-m68k/%.o,$(QUIRC_SRCS)) -.PHONY: all test cli smoke diff m68k m68k-docker gui gui-docker gui-smoke qr-onhw qr-onhw-docker qr-onhw-smoke serialtest-m68k serialtest-m68k-docker copperline-smoke pbkdf2-bench clean +.PHONY: all test cli smoke diff m68k m68k-docker gui gui-docker gui-smoke qr-onhw qr-onhw-docker qr-onhw-smoke serialtest-m68k serialtest-m68k-docker copperline-smoke pbkdf2-bench asm-bench amissl-bench clean all: test cli @@ -211,6 +211,18 @@ copperline-smoke: serialtest-m68k-docker pbkdf2-bench: sh tests/copperline/bench.sh +# Compare the portable C SHA-1 compress function against the hand-written +# 68000 asm one (#47), same boot/CPU. Dev-only, verifies the asm path is +# actually worth its runtime-dispatch complexity. +asm-bench: + sh tests/copperline/asm-bench.sh + +# Compare AmiSSL's PBKDF2-HMAC-SHA1 against the builtin one, same boot/CPU +# (issue #85 groundwork). Dev-only: needs tests/gui/.env (AMIAUTH_WB_HDD/ +# AMIAUTH_ROM) and the AmiSSL SDK (auto-fetched, cached). +amissl-bench: + sh tests/copperline/amissl-bench.sh + # --- guide: AmigaGuide user documentation, generated from userdocs/ ---------- # userdocs/ is the single source of truth for user docs (published as the # MkDocs site); this converts it for on-Amiga reading (MultiView/AmigaGuide). diff --git a/tests/copperline/amissl-bench.sh b/tests/copperline/amissl-bench.sh new file mode 100755 index 0000000..19b71c5 --- /dev/null +++ b/tests/copperline/amissl-bench.sh @@ -0,0 +1,111 @@ +#!/bin/sh +# amissl-bench.sh — compare AmiSSL's PKCS5_PBKDF2_HMAC_SHA1 against our own +# pbkdf2_hmac_sha1, same boot, same CPU, same params. Dev-only groundwork for +# issue #85 ("optional AmiSSL crypto provider") — informs whether that's +# worth building, does not itself change anything shipped. Invoked by +# `make amissl-bench`. +# +# AmiSSL needs 68020+/OS3.0+ (no 68000 path exists), so unlike pbkdf2-bench +# this boots an accelerated profile, not the plain A500 baseline. +# +# Boots from a copy-on-write clone of the same Workbench 3.2 install used by +# tests/gui/gui-smoke.sh (tests/gui/.env: AMIAUTH_WB_HDD/AMIAUTH_ROM), not a +# synthetic minimal disk: AmiSSL's OpenAmiSSLTags() reliably crashes ramlib +# (CPU exception ACPU_CHK, 0x80000006) on a from-scratch boot volume that +# only has amisslmaster.library + the CPU-specific amissl_v*.library copied +# into LIBS: — it also needs the `AmiSSL:` assign (`Assign AmiSSL: SYS:AmiSSL` +# + `Assign LIBS: AmiSSL:Libs ADD`, done by this WB's S:User-Startup) that a +# minimal boot doesn't set up, evidently for something InitAmiSSL() reads +# even with no networking (config/cert store lookup, most likely). The real +# WB install already has AmiSSL installed and its assigns wired up, so this +# reuses that known-good environment instead of re-deriving it. Confirmed +# 2026-07-28. +set -eu + +HERE=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "$HERE/../.." && pwd) +[ -f "$ROOT/tests/gui/.env" ] && . "$ROOT/tests/gui/.env" + +COPPERLINE=${COPPERLINE:-copperline} +KICK=${KICK:-${AMIAUTH_ROM:-}} +WB=${WB:-${AMIAUTH_WB_HDD:-}} +IMG=${AMIGA_GCC_IMAGE:-stefanreinauer/amiga-gcc:latest} +BENCH=${BENCH:-90} # emulated seconds; WB boot + both PBKDF2 runs need headroom + +[ -n "$KICK" ] && [ -e "$KICK" ] || { echo "FAIL: Kickstart ROM missing (KICK= / AMIAUTH_ROM in tests/gui/.env): '$KICK'" >&2; exit 2; } +[ -n "$WB" ] && [ -d "$WB" ] || { echo "FAIL: Workbench dir missing (WB= / AMIAUTH_WB_HDD in tests/gui/.env): '$WB'" >&2; exit 2; } +[ -f "$WB/S/User-Startup" ] || { echo "FAIL: $WB/S/User-Startup missing (need the AmiSSL assign it sets up)" >&2; exit 2; } +command -v "$COPPERLINE" >/dev/null || { echo "FAIL: $COPPERLINE not found" >&2; exit 2; } + +# Fetch (or reuse the cached) AmiSSL SDK — only the Developer headers + +# libamisslstubs.a are needed to link the bench binary; the runtime +# amisslmaster.library/amissl_v*.library come from the WB clone itself. +sdk_out=$("$HERE/fetch-amissl-sdk.sh") +SDK_DEV=$(echo "$sdk_out" | sed -n 1p) +[ -d "$SDK_DEV" ] || { echo "FAIL: fetch-amissl-sdk.sh did not return a valid SDK path" >&2; exit 1; } + +# Build the bench binary (m68k, -O2, 68020 target — AmiSSL has no 68000 +# path). hmac.c defines the SHA-256/512 HMAC variants unconditionally, so +# sha256.c/sha512.c must link even though this benchmark only exercises SHA-1. +docker run --rm --platform linux/amd64 \ + -v "$ROOT":/work -v "$SDK_DEV":/amissl-sdk -w /work "$IMG" sh -lc \ + 'PATH=/opt/amiga/bin:$PATH m68k-amigaos-gcc -std=c99 -O2 -Wall -m68020 -noixemul \ + -Isrc/core -I/amissl-sdk/include \ + src/core/pbkdf2.c src/core/hmac.c src/core/sha1.c \ + src/core/sha256.c src/core/sha512.c \ + tests/copperline/amisslbench.c \ + -L/amissl-sdk/lib/AmigaOS3 -lamisslstubs \ + -o build/amisslbench' + +T=$(mktemp -d) +OUT=$(mktemp) +trap 'rm -rf "$T" "$OUT"' EXIT INT TERM + +# --- clone the WB (copy-on-write) and stage the bench binary ----------------- +cp -Rc "$WB" "$T/boot" 2>/dev/null || cp -R "$WB" "$T/boot" +cp "$ROOT/build/amisslbench" "$T/boot/bench" + +# Run right after S:User-Startup (which sets up the AmiSSL:/LIBS: assigns) +# and before LoadWB — a Workbench screen isn't needed for this, so skip the +# time it'd take to load one. +SEQ="$T/boot/S/Startup-Sequence" +awk '/^LoadWB/ { print "SYS:bench" } { print }' "$SEQ" > "$SEQ.new" && mv "$SEQ.new" "$SEQ" +grep -q '^SYS:bench$' "$SEQ" || { echo "FAIL: could not patch Startup-Sequence (no LoadWB line?)" >&2; exit 1; } + +cat > "$T/cfg.toml" <"$OUT" 2>/dev/null || true + +builtin_line=$(tr -d '\r' <"$OUT" | grep '^BUILTIN ' | head -1 || true) +amissl_line=$(tr -d '\r' <"$OUT" | grep '^AMISSL ' | head -1 || true) +err_line=$(tr -d '\r' <"$OUT" | grep '^ERR: ' | head -1 || true) + +[ -n "$builtin_line" ] || { echo "FAIL: no BUILTIN result (boot/timer issue?)" >&2; cat "$OUT" >&2; exit 1; } +[ -n "$err_line" ] && echo "note: $err_line" >&2 +[ -n "$amissl_line" ] || { echo "FAIL: no AMISSL result — see note above" >&2; exit 1; } + +echo "raw: $builtin_line" +echo "raw: $amissl_line" + +report() { + label=$1 line=$2 + iters=$(echo "$line" | sed -n 's/.*iters=\([0-9]*\).*/\1/p') + ticks=$(echo "$line" | sed -n 's/.*ticks=\([0-9]*\).*/\1/p') + freq=$(echo "$line" | sed -n 's/.*freq=\([0-9]*\).*/\1/p') + awk -v l="$label" -v i="$iters" -v t="$ticks" -v f="$freq" 'BEGIN { + sec = t / f; rate = i / sec; + printf "%-8s PBKDF2(dkLen=64): %.1f iters/sec (%d iters in %.2fs)\n", l, rate, i, sec; + }' +} +report "BUILTIN" "$builtin_line" +report "AMISSL" "$amissl_line" diff --git a/tests/copperline/amisslbench.c b/tests/copperline/amisslbench.c new file mode 100644 index 0000000..f0d1052 --- /dev/null +++ b/tests/copperline/amisslbench.c @@ -0,0 +1,124 @@ +/* amisslbench.c — compare AmiSSL's PKCS5_PBKDF2_HMAC_SHA1 against our own + * pbkdf2_hmac_sha1 on the same boot, same CPU, same iteration count. Dev-only + * groundwork for issue #85 ("optional AmiSSL crypto provider") — this is not + * shipped, and AmiAuth stays zero-dependency at runtime regardless of what + * this benchmark finds. See tests/copperline/amissl-bench.sh. + * + * AmiSSL init mirrors the (in-production) pattern in + * micropython/ports/amiga/amiga_ssl.c: OpenLibrary("amisslmaster.library"), + * then OpenAmiSSLTagList. AmiSSL_SocketBase is omitted deliberately — the + * SDK's own Autodoc (amissl.doc/InitAmiSSLA) says it "can be omitted" when + * the caller "doesn't need any networking functionality" (defaults to NULL), + * which is our case: PBKDF2 never touches the network. + * + * Timing follows pbkdf2bench.c exactly (timer.device EClock, same params, + * same RawPutChar serial output) so the two numbers are directly comparable. */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pbkdf2.h" + +struct Device *TimerBase; +struct Library *AmiSSLMasterBase; +struct Library *AmiSSLBase; +struct Library *AmiSSLExtBase; + +/* --- serial output via exec RawPutChar (LVO -516) --- */ +static void raw_put(char c) +{ + void *SysBase = *(void **)4UL; + register long d0 __asm__("d0") = (unsigned char)c; + register void *a6 __asm__("a6") = SysBase; + __asm__ volatile("jsr -516(%%a6)" : : "r"(d0), "r"(a6) + : "d1", "a0", "a1", "cc", "memory"); +} +static void raw_str(const char *s) { while (*s) raw_put(*s++); } +static void raw_u32(uint32_t v) +{ + char b[12]; int n = 0; + if (!v) b[n++] = '0'; + while (v) { b[n++] = (char)('0' + v % 10); v /= 10; } + while (n) raw_put(b[--n]); +} + +/* Same inputs pbkdf2bench.c uses, so the two runs are apples-to-apples. */ +static const uint8_t pass[8] = { 'p','a','s','s','w','o','r','d' }; +static const uint8_t salt[16] = { 0 }; +static const uint32_t N = 600; + +static void report(const char *label, uint32_t iters, uint32_t ticks, uint32_t freq) +{ + raw_str(label); raw_str(" iters="); raw_u32(iters); + raw_str(" ticks="); raw_u32(ticks); + raw_str(" freq="); raw_u32(freq); + raw_str("\r\n"); +} + +int main(void) +{ + struct MsgPort *port; + struct timerequest *tr; + struct EClockVal t0, t1; + ULONG freq; + uint8_t dk[64]; + LONG rc; + + raw_str("BEGIN\r\n"); + + port = CreateMsgPort(); + tr = (struct timerequest *)CreateIORequest(port, sizeof *tr); + if (OpenDevice((STRPTR)TIMERNAME, UNIT_ECLOCK, (struct IORequest *)tr, 0) != 0) { + raw_str("ERR: no timer.device\r\n"); + return 1; + } + TimerBase = tr->tr_node.io_Device; + + /* --- our own implementation --- */ + freq = ReadEClock(&t0); + pbkdf2_hmac_sha1(pass, sizeof pass, salt, sizeof salt, N, dk, sizeof dk); + ReadEClock(&t1); + report("BUILTIN", N, t1.ev_lo - t0.ev_lo, freq); + + /* --- AmiSSL --- */ + AmiSSLMasterBase = OpenLibrary((STRPTR)"amisslmaster.library", 5); + if (AmiSSLMasterBase == NULL) { + raw_str("ERR: no amisslmaster.library\r\n"); + goto done; + } + + rc = OpenAmiSSLTags(AMISSL_CURRENT_VERSION, + AmiSSL_UsesOpenSSLStructs, FALSE, + AmiSSL_GetAmiSSLBase, (ULONG)&AmiSSLBase, + AmiSSL_GetAmiSSLExtBase, (ULONG)&AmiSSLExtBase, + TAG_DONE); + if (rc != 0 || AmiSSLBase == NULL) { + raw_str("ERR: OpenAmiSSLTags failed rc="); raw_u32((uint32_t)rc); raw_str("\r\n"); + CloseLibrary(AmiSSLMasterBase); + AmiSSLMasterBase = NULL; + goto done; + } + + freq = ReadEClock(&t0); + PKCS5_PBKDF2_HMAC_SHA1((const char *)pass, sizeof pass, salt, sizeof salt, + (int)N, sizeof dk, dk); + ReadEClock(&t1); + report("AMISSL", N, t1.ev_lo - t0.ev_lo, freq); + + CloseAmiSSL(); + AmiSSLBase = NULL; + AmiSSLExtBase = NULL; + CloseLibrary(AmiSSLMasterBase); + AmiSSLMasterBase = NULL; + +done: + raw_str("END\r\n"); + CloseDevice((struct IORequest *)tr); + return 0; +} diff --git a/tests/copperline/asm-bench.sh b/tests/copperline/asm-bench.sh new file mode 100755 index 0000000..42d11aa --- /dev/null +++ b/tests/copperline/asm-bench.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# asm-bench.sh — compare the portable C SHA-1 compress function against the +# hand-written 68000 asm one (#47), via PBKDF2-HMAC-SHA1, same boot, same +# CPU, same params, real 68000 (A500). Dev-only groundwork/verification for +# crypto_dispatch.h's runtime asm dispatch — does not itself change anything +# shipped. Invoked by `make asm-bench`. +# +# Needs copperline (or COPPERLINE= pointing at another build — see run.sh's +# header for why you might need one), docker (the amiga-gcc image), and a +# 512 KiB Kickstart ROM (timer.device EClock is not available under the +# bundled AROS, so this one dev tool needs a real ROM — override with KICK=). +set -eu + +HERE=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "$HERE/../.." && pwd) +COPPERLINE=${COPPERLINE:-copperline} +KICK=${KICK:-/Users/simond/Documents/Amiberry/Roms/amiga-os-310-a600.rom} +IMG=${AMIGA_GCC_IMAGE:-stefanreinauer/amiga-gcc:latest} + +[ -e "$KICK" ] || { echo "FAIL: need a Kickstart ROM (set KICK=)" >&2; exit 2; } +command -v "$COPPERLINE" >/dev/null || { echo "FAIL: $COPPERLINE not found" >&2; exit 2; } + +# Build the bench binary (m68k, -O2 as production, plain 68000 — same +# baseline the asm targets). sha256.c/sha512.c must link for the same reason +# noted in bench.sh (hmac.c defines the SHA-256/512 HMAC variants +# unconditionally). +docker run --rm --platform linux/amd64 -v "$ROOT":/work -w /work "$IMG" sh -lc \ + 'PATH=/opt/amiga/bin:$PATH m68k-amigaos-gcc -std=c99 -O2 -Wall -m68000 -noixemul \ + -Isrc/core src/core/pbkdf2.c src/core/hmac.c src/core/sha1.c \ + src/core/sha256.c src/core/sha512.c src/core/sha1_asm.s \ + tests/copperline/asmbench.c -o build/asmbench' + +T=$(mktemp -d) +OUT=$(mktemp) +trap 'rm -rf "$T" "$OUT"' EXIT INT TERM +mkdir -p "$T/sys/C" "$T/sys/S" +cp "$ROOT/build/asmbench" "$T/sys/C/bench" +printf 'bench\n' > "$T/sys/S/Startup-Sequence" +cat > "$T/m.toml" <"$OUT" 2>/dev/null || true + +c_line=$(tr -d '\r' <"$OUT" | grep '^PBKDF2 impl=c ' | head -1 || true) +asm_line=$(tr -d '\r' <"$OUT" | grep '^PBKDF2 impl=asm ' | head -1 || true) + +[ -n "$c_line" ] || { echo "FAIL: no C result (boot/timer issue?)" >&2; cat "$OUT" >&2; exit 1; } +[ -n "$asm_line" ] || { echo "FAIL: no asm result" >&2; cat "$OUT" >&2; exit 1; } + +echo "raw: $c_line" +echo "raw: $asm_line" + +report() { + label=$1 line=$2 + iters=$(echo "$line" | sed -n 's/.*iters=\([0-9]*\).*/\1/p') + ticks=$(echo "$line" | sed -n 's/.*ticks=\([0-9]*\).*/\1/p') + freq=$(echo "$line" | sed -n 's/.*freq=\([0-9]*\).*/\1/p') + awk -v l="$label" -v i="$iters" -v t="$ticks" -v f="$freq" 'BEGIN { + sec = t / f; rate = i / sec; + printf "%-4s PBKDF2(dkLen=64) on a stock 68000: %.1f iters/sec (%d iters in %.2fs)\n", l, rate, i, sec; + }' +} +report "C" "$c_line" +report "asm" "$asm_line" diff --git a/tests/copperline/asmbench.c b/tests/copperline/asmbench.c new file mode 100644 index 0000000..f8d1bfa --- /dev/null +++ b/tests/copperline/asmbench.c @@ -0,0 +1,90 @@ +/* asmbench.c — compare the C reference vs. the hand-written SHA-1 asm (#47) + * via PBKDF2-HMAC-SHA1, on a real 68000 (under Copperline's A500) using + * timer.device EClock. Times both paths back-to-back in one run so both + * numbers come from the same boot/hardware pass. Dev tool only, not shipped + * (mirrors pbkdf2bench.c's own scope note). + * + * ChaCha20 has no asm path to compare - a hand-written attempt measured + * ~17% slower than its C reference here, so it's not part of this + * benchmark; see src/core/crypto_dispatch.h. */ +#include +#include + +#include +#include + +#include "pbkdf2.h" +#include "crypto_dispatch.h" + +struct Device *TimerBase; + +extern void sha1_compress_asm(uint32_t state[5], const uint8_t block[SHA1_BLOCK_SIZE]); + +/* --- serial output via exec RawPutChar (LVO -516) --- */ +static void raw_put(char c) +{ + void *SysBase = *(void **)4UL; + register long d0 __asm__("d0") = (unsigned char)c; + register void *a6 __asm__("a6") = SysBase; + __asm__ volatile("jsr -516(%%a6)" : : "r"(d0), "r"(a6) + : "d1", "a0", "a1", "cc", "memory"); +} +static void raw_str(const char *s) { while (*s) raw_put(*s++); } +static void raw_u32(uint32_t v) +{ + char b[12]; int n = 0; + if (!v) b[n++] = '0'; + while (v) { b[n++] = (char)('0' + v % 10); v /= 10; } + while (n) raw_put(b[--n]); +} + +static ULONG g_freq; + +static ULONG ticks_since(struct EClockVal *t0) +{ + struct EClockVal t1; + ReadEClock(&t1); + return t1.ev_lo - t0->ev_lo; /* assumes ev_hi unchanged over the window */ +} + +int main(void) +{ + struct MsgPort *port; + struct timerequest *tr; + struct EClockVal t0; + const uint8_t pass[8] = { 'p','a','s','s','w','o','r','d' }; + const uint8_t salt[16] = { 0 }; + uint8_t dk[64]; + const uint32_t PBKDF2_N = 600; /* same as pbkdf2bench.c */ + + raw_str("BEGIN\r\n"); + port = CreateMsgPort(); + tr = (struct timerequest *)CreateIORequest(port, sizeof *tr); + if (OpenDevice((STRPTR)TIMERNAME, UNIT_ECLOCK, (struct IORequest *)tr, 0) != 0) { + raw_str("ERR: no timer.device\r\n"); + return 1; + } + TimerBase = tr->tr_node.io_Device; + g_freq = ReadEClock(&t0); + + /* --- PBKDF2-HMAC-SHA1 (dominated by sha1_compress calls) --- */ + g_sha1_compress = sha1_compress_c; + ReadEClock(&t0); + pbkdf2_hmac_sha1(pass, sizeof pass, salt, sizeof salt, PBKDF2_N, dk, sizeof dk); + raw_str("PBKDF2 impl=c iters="); raw_u32(PBKDF2_N); + raw_str(" ticks="); raw_u32(ticks_since(&t0)); + raw_str(" freq="); raw_u32(g_freq); + raw_str("\r\n"); + + g_sha1_compress = sha1_compress_asm; + ReadEClock(&t0); + pbkdf2_hmac_sha1(pass, sizeof pass, salt, sizeof salt, PBKDF2_N, dk, sizeof dk); + raw_str("PBKDF2 impl=asm iters="); raw_u32(PBKDF2_N); + raw_str(" ticks="); raw_u32(ticks_since(&t0)); + raw_str(" freq="); raw_u32(g_freq); + raw_str("\r\n"); + + raw_str("END\r\n"); + CloseDevice((struct IORequest *)tr); + return 0; +} diff --git a/tests/copperline/fetch-amissl-sdk.sh b/tests/copperline/fetch-amissl-sdk.sh new file mode 100755 index 0000000..d58f5c4 --- /dev/null +++ b/tests/copperline/fetch-amissl-sdk.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# fetch-amissl-sdk.sh — fetch + unpack the AmiSSL v5 SDK and OS3 runtime +# package, for the dev-only AmiSSL-vs-builtin PBKDF2 benchmark (issue #85 +# groundwork). Not part of the shipped build — AmiAuth stays zero-dependency +# at runtime; this only feeds a local benchmark tool. +# +# Fetches two archives from the same pinned release: +# - AmiSSL--SDK.lha — headers + libamisslstubs.a, for linking the +# bench binary against m68k-amigaos-gcc. +# - AmiSSL--OS3.lha — the actual runtime amisslmaster.library and the +# CPU-specific amissl_v*.library, which must be staged onto the guest's +# boot volume (LIBS:/LIBS:AmiSSL/) for OpenLibrary() to succeed. The SDK +# alone does not include these. +# +# Idempotent: downloads + extracts only when the pinned-version directory is +# missing. Verifies SHA-256 on both archives. On success, prints two lines to +# stdout: the SDK's Developer dir, then the OS3 package's AmiSSL dir. +# +# Usage: +# tests/copperline/fetch-amissl-sdk.sh +# +# Environment: +# AMISSL_VERSION override the pinned version (default 5.27) +# AMISSL_CACHE_DIR cache root (default $HOME/.cache/amissl-sdk) +# +# Exit codes: 0 ok, 1 download/verify/extract failure, 2 usage. +set -euo pipefail + +AMISSL_VERSION="${AMISSL_VERSION:-5.27}" +AMISSL_CACHE_DIR="${AMISSL_CACHE_DIR:-$HOME/.cache/amissl-sdk}" + +# Pinned SHA-256s for both archives. If AMISSL_VERSION is changed, matching +# hashes must be supplied via AMISSL_SDK_SHA256/AMISSL_OS3_SHA256 — refuse to +# silently fetch an unpinned version. +case "$AMISSL_VERSION" in + 5.27) + PINNED_SDK_SHA256="5003bef8c5930354d16b0ce7196d71b2811891c42fad38a9238c5ce4098ad42a" + PINNED_OS3_SHA256="722476cfbf21c0caf6ff4a77e32ee8baf67d3045db0e9ff2ccce6c782bed5847" + ;; + *) + PINNED_SDK_SHA256="${AMISSL_SDK_SHA256:-}" + PINNED_OS3_SHA256="${AMISSL_OS3_SHA256:-}" + ;; +esac + +if [ -z "$PINNED_SDK_SHA256" ] || [ -z "$PINNED_OS3_SHA256" ]; then + echo "fetch-amissl-sdk.sh: AMISSL_VERSION=$AMISSL_VERSION has no pinned" >&2 + echo " SHA-256s. Set AMISSL_SDK_SHA256 and AMISSL_OS3_SHA256 to proceed." >&2 + exit 2 +fi + +version_dir="$AMISSL_CACHE_DIR/amissl-${AMISSL_VERSION}" +sdk_root="$version_dir/sdk" +os3_root="$version_dir/os3" +stamp="$version_dir/.ok" + +sdk_dev="$sdk_root/AmiSSL/Developer" +os3_dir="$os3_root/AmiSSL" + +if [ -f "$stamp" ] && [ -d "$sdk_dev" ] && [ -d "$os3_dir" ]; then + echo "$sdk_dev" + echo "$os3_dir" + exit 0 +fi + +need() { command -v "$1" >/dev/null 2>&1 || { echo "fetch-amissl-sdk.sh: missing tool '$1'" >&2; exit 1; }; } +need curl +need lha +if command -v sha256sum >/dev/null 2>&1; then + sha_cmd="sha256sum" +elif command -v shasum >/dev/null 2>&1; then + sha_cmd="shasum -a 256" +else + echo "fetch-amissl-sdk.sh: need sha256sum or shasum on PATH" >&2 + exit 1 +fi + +fetch_verify_extract() { + local name="$1" url="$2" pinned="$3" dest_dir="$4" + local archive_path="$version_dir/$name" + mkdir -p "$version_dir" "$dest_dir" + + echo "fetch-amissl-sdk.sh: fetching $name" >&2 + curl -fSL --retry 3 -o "$archive_path" "$url" + + local got + got=$($sha_cmd "$archive_path" | awk '{print $1}') + if [ "$got" != "$pinned" ]; then + echo "fetch-amissl-sdk.sh: checksum mismatch for $name" >&2 + echo " expected $pinned" >&2 + echo " got $got" >&2 + rm -f "$archive_path" + exit 1 + fi + + (cd "$dest_dir" && lha xq "$archive_path") +} + +fetch_verify_extract "AmiSSL-${AMISSL_VERSION}-SDK.lha" \ + "https://github.com/jens-maus/amissl/releases/download/${AMISSL_VERSION}/AmiSSL-${AMISSL_VERSION}-SDK.lha" \ + "$PINNED_SDK_SHA256" "$sdk_root" + +fetch_verify_extract "AmiSSL-${AMISSL_VERSION}-OS3.lha" \ + "https://github.com/jens-maus/amissl/releases/download/${AMISSL_VERSION}/AmiSSL-${AMISSL_VERSION}-OS3.lha" \ + "$PINNED_OS3_SHA256" "$os3_root" + +if [ ! -d "$sdk_dev/include/openssl" ] \ + || [ ! -f "$sdk_dev/lib/AmigaOS3/libamisslstubs.a" ]; then + echo "fetch-amissl-sdk.sh: unexpected SDK layout under $sdk_root" >&2 + exit 1 +fi +if [ ! -f "$os3_dir/Libs/AmigaOS3/amisslmaster.library" ] \ + || [ ! -f "$os3_dir/Libs/AmigaOS3/AmiSSL/68020-40/amissl_v362.library" ]; then + echo "fetch-amissl-sdk.sh: unexpected OS3 package layout under $os3_root" >&2 + exit 1 +fi + +touch "$stamp" +echo "$sdk_dev" +echo "$os3_dir"