Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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).
Expand Down
111 changes: 111 additions & 0 deletions tests/copperline/amissl-bench.sh
Original file line number Diff line number Diff line change
@@ -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" <<EOF
[machine]
profile = "A1200"
[memory]
fast = "8M"
[ide]
master = { path = "$T/boot", name = "Workbench" }
[emulation]
warp_speed = "max"
EOF

"$COPPERLINE" --config "$T/cfg.toml" --noaudio --serial stdout --benchmark-until "$BENCH" "$KICK" \
>"$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"
124 changes: 124 additions & 0 deletions tests/copperline/amisslbench.c
Original file line number Diff line number Diff line change
@@ -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 <exec/types.h>
#include <devices/timer.h>
#include <libraries/amisslmaster.h>

#include <proto/exec.h>
#include <proto/timer.h>
#include <proto/amisslmaster.h>
#include <amissl/tags.h>
#include <openssl/evp.h>

#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;
}
80 changes: 80 additions & 0 deletions tests/copperline/asm-bench.sh
Original file line number Diff line number Diff line change
@@ -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" <<EOF
[machine]
profile = "A500"
[cpu]
model = "68000"
[memory]
chip = "512K"
slow = "512K"
[floppy]
drives = 1
[[filesys]]
path = "$T/sys"
volume = "Bench"
bootpri = 10
[emulation]
warp_speed = "max"
EOF

"$COPPERLINE" --config "$T/m.toml" --noaudio --serial stdout --benchmark-until 300 "$KICK" \
>"$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"
Loading