commit 6a3713883a8f2ab04bb047dfa49439920061c50f from: Rohan Verma date: Thu Jul 2 12:26:00 2026 UTC add signify commit - c86fbcd84d03494d9288500e642ba3a7c93dfac1 commit + 6a3713883a8f2ab04bb047dfa49439920061c50f blob - e700701617b01129f0b60469b09a780fd1b971d3 blob + 24691e3c7e5981dd0e9b3a08b7d00937bdce5bfe --- .gitignore +++ .gitignore @@ -4,3 +4,4 @@ milna.exe deps-win-x86_64/ milna-macos-app.zip milna.app/ +milna.sec blob - 3e119da204e6c6355e61fb2055c7f06ddcc5fb9e blob + f5d5903678900c6e3877a59af8e9f3546bfe684d --- build.sh +++ build.sh @@ -4,6 +4,55 @@ INC="-Ilib -Isrc" CORE="src/ui.c src/audio.c src/aproc.c src/net.c src/update.c lib/microui.c" CFLAGS="-Os -Wall -ffunction-sections -fdata-sections" +SIGNDIR="${SIGNDIR:-$HOME/.signify}" +SIGNKEYNAME="${SIGNKEYNAME:-rverma-2026}" +SIGNKEY="${SIGNKEY:-$SIGNDIR/$SIGNKEYNAME.sec}" +SIGNPUB="${SIGNPUB:-$SIGNDIR/$SIGNKEYNAME.pub}" +SIGNIFY=signify +command -v "$SIGNIFY" >/dev/null 2>&1 || SIGNIFY=signify-openbsd + +if [ "$1" = "release" ]; then + [ -f "$SIGNPUB" ] || { echo "no pubkey at $SIGNPUB"; exit 1; } + [ -f "$SIGNKEY" ] || { echo "no seckey at $SIGNKEY"; exit 1; } + echo ">> mac build + sign" + "$0" + echo ">> windows build + sign" + "$0" win + cat <<'NOTE' + +>> upload to VPS /var/www/mi/ (rename binaries; .sig names already match): + milna -> milna-macos (+ milna-macos.sig) + milna.exe -> milna-windows.exe (+ milna-windows.exe.sig) + milna-macos-app.zip (+ milna-macos-app.zip.sig) + then bump version LAST: + echo "X.Y" | ssh USER@mi.rsap.sh 'doas tee /var/www/mi/version' +NOTE + exit 0 +fi + +PUBDEF="" +if [ -f "$SIGNPUB" ]; then + bytes=$(sed -n 2p "$SIGNPUB" | base64 -d 2>/dev/null | \ + dd bs=1 skip=10 count=32 2>/dev/null | od -An -tu1 | \ + tr -s ' \n' ',' | sed 's/^,//;s/,$//') + if [ -n "$bytes" ]; then + PUBDEF="-DMILNA_PUBKEY=$bytes" + echo "pubkey: baked in from $SIGNPUB" + fi +fi +CFLAGS="$CFLAGS $PUBDEF" + +sign_artifact() { + [ -f "$SIGNKEY" ] || return 0 + signame="${2:-$1}" + command -v "$SIGNIFY" >/dev/null 2>&1 || { + echo " (signify not found — '$1' UNSIGNED; brew install signify-osx)" + return 0; } + "$SIGNIFY" -S -c '' -s "$SIGNKEY" -m "$1" -x "$signame.sig" 2>/dev/null && \ + echo " signed $1 -> $signame.sig" || \ + echo " (signing failed for $1)" +} + if [ "$1" = "win" ]; then WARCH=${WINARCH:-x86_64} case "$WARCH" in @@ -11,7 +60,6 @@ if [ "$1" = "win" ]; then arm64) TRIPLE=aarch64-w64-mingw32 ;; *) echo "WINARCH: x86_64 | arm64"; exit 1 ;; esac - if [ -n "$WINCC" ]; then WCC=$WINCC elif command -v $TRIPLE-clang >/dev/null 2>&1; then WCC=$TRIPLE-clang elif command -v $TRIPLE-gcc-posix >/dev/null 2>&1; then WCC=$TRIPLE-gcc-posix @@ -23,7 +71,7 @@ if [ "$1" = "win" ]; then exit 1 fi PFX=${WINPFX:-./deps-win-$WARCH} - [ -d "$PFX" ] || PFX=/tmp/w64 # sandbox fallback + [ -d "$PFX" ] || PFX=/tmp/w64 [ -d "$PFX" ] || { echo "deps missing: run ./win-deps.sh $WARCH"; exit 1; } ICON_OBJ="" @@ -69,13 +117,14 @@ if [ "$1" = "win" ]; then -static -mwindows -Wl,--gc-sections $TRIPLE-strip milna.exe 2>/dev/null || true echo "built ./milna.exe ($(du -h milna.exe | cut -f1))" + sign_artifact milna.exe milna-windows.exe exit 0 fi CC=${CC:-cc} OUT=milna case "$(uname -s)" in -Darwin) +Darwin) OPUS=$(brew --prefix opus 2>/dev/null || echo /opt/homebrew/opt/opus) SPX=$(brew --prefix speexdsp 2>/dev/null || echo /opt/homebrew/opt/speexdsp) SODIUM=$(brew --prefix libsodium 2>/dev/null || echo /opt/homebrew/opt/libsodium) @@ -91,6 +140,7 @@ Darwin) -Wl,-dead_strip strip "$OUT" 2>/dev/null || true echo "built ./$OUT ($(du -h $OUT | cut -f1))" + sign_artifact "$OUT" milna-macos APP=milna.app rm -rf "$APP" @@ -146,13 +196,14 @@ PLIST rm -f milna-macos-app.zip if command -v zip >/dev/null 2>&1; then zip -qry milna-macos-app.zip "$APP" - echo "zipped ./milna-macos-app.zip (upload to /var/www/milna/)" + echo "zipped ./milna-macos-app.zip (upload to /var/www/mi/)" + sign_artifact milna-macos-app.zip else echo "note: 'zip' not found — install it to produce the update zip" fi exit 0 ;; -Linux) +Linux) $CC $CFLAGS $INC $(pkg-config --cflags opus speexdsp vpx libsodium) \ src/plat_x11.c src/vid.c src/cap_stub.c src/cam_stub.c $CORE -o $OUT \ $(pkg-config --libs opus speexdsp vpx libsodium) \ blob - e9cb2be653e83b08633356e88ac51293627f7736 blob + b46cf5fa3e8cb58fa688e9740ca1303b6e0ac826 --- src/update.c +++ src/update.c @@ -4,7 +4,81 @@ #include #include #include "update.h" +#include +static int run_argv(char *const argv[]); + +#ifndef MILNA_PUBKEY +#define MILNA_PUBKEY 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, \ + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 +#endif +static const unsigned char SIGN_PUBKEY[32] = { MILNA_PUBKEY }; + +static int signing_enabled(void) { + for (int i = 0; i < 32; i++) if (SIGN_PUBKEY[i]) return 1; + return 0; +} + +static int signify_sig_bytes(const char *path, unsigned char sig[64]) { + FILE *f = fopen(path, "rb"); + if (!f) return -1; + char line[512]; int got = 0; + while (fgets(line, sizeof(line), f)) { + if (!strncmp(line, "untrusted comment:", 18)) continue; + char *nl = strpbrk(line, "\r\n"); if (nl) *nl = 0; + unsigned char raw[128]; size_t rawlen = 0; + if (sodium_base642bin(raw, sizeof(raw), line, strlen(line), + NULL, &rawlen, NULL, sodium_base64_VARIANT_ORIGINAL) != 0) + break; + if (rawlen != 2 + 8 + 64) break; + memcpy(sig, raw + 10, 64); + got = 1; break; + } + fclose(f); + return got ? 0 : -1; +} + +static int verify_file(const char *file, const char *sigpath) { + if (!signing_enabled()) return 0; + unsigned char sig[64]; + if (signify_sig_bytes(sigpath, sig) != 0) { + fprintf(stderr, "[milna] update: signature file missing/invalid\n"); + return -1; + } + FILE *f = fopen(file, "rb"); + if (!f) return -1; + fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET); + if (sz <= 0 || sz > 64 * 1024 * 1024) { fclose(f); return -1; } + unsigned char *buf = malloc((size_t)sz); + if (!buf) { fclose(f); return -1; } + size_t rd = fread(buf, 1, (size_t)sz, f); + fclose(f); + if (rd != (size_t)sz) { free(buf); return -1; } + int ok = crypto_sign_verify_detached(sig, buf, (size_t)sz, + SIGN_PUBKEY) == 0; + free(buf); + if (!ok) fprintf(stderr, "[milna] update: SIGNATURE VERIFY FAILED — " + "refusing to install\n"); + return ok ? 0 : -1; +} + +static int fetch_and_verify(const char *url, const char *localfile) { + if (!signing_enabled()) return 0; + char sigurl[1300], sigfile[1200]; + snprintf(sigurl, sizeof(sigurl), "%s.sig", url); + snprintf(sigfile, sizeof(sigfile), "%s.sig", localfile); + char *sv[] = { (char*)"curl", (char*)"-fsSL", (char*)"--max-time", + (char*)"30", sigurl, (char*)"-o", sigfile, NULL }; + if (run_argv(sv) != 0) { + fprintf(stderr, "[milna] update: could not fetch signature %s\n", + sigurl); + return -1; + } + int r = verify_file(localfile, sigfile); + remove(sigfile); + return r; +} + static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER; #ifndef __APPLE__ static void *update_run_blocking_thunk(void *a); @@ -31,8 +105,8 @@ static void *update_run_blocking_thunk(void *a); #define VER_URL "https://mi.rsap.sh/version" #define BIN_BASE "https://mi.rsap.sh/milna-" -static char g_self[1024]; /* path to the running executable */ -static char g_new[1024]; /* path to the downloaded update */ +static char g_self[1024]; +static char g_new[1024]; static int bundle_path(char *out, size_t cap) { #ifdef __APPLE__ @@ -41,14 +115,14 @@ static int bundle_path(char *out, size_t cap) { snprintf(tmp, sizeof(tmp), "%s", g_self); char *p = strstr(tmp, marker); if (!p) return 0; - p[4] = 0; /* cut right after ".app" */ + p[4] = 0; snprintf(out, cap, "%s", tmp); return 1; #else (void)out; (void)cap; return 0; #endif } -static volatile int g_ready; /* an update is downloaded & waiting */ +static volatile int g_ready; static char g_latest[32]; static char g_localver[32]; @@ -74,6 +148,7 @@ static void self_path(char *out, size_t cap) { #elif defined(__APPLE__) char raw[1024]; uint32_t sz = sizeof(raw); if (_NSGetExecutablePath(raw, &sz) != 0) { out[0] = 0; return; } + if (!realpath(raw, out)) snprintf(out, cap, "%s", raw); #else ssize_t n = readlink("/proc/self/exe", out, cap - 1); @@ -84,6 +159,7 @@ static void self_path(char *out, size_t cap) { #ifdef _WIN32 #include static int run_argv(char *const argv[]) { + intptr_t r = _spawnvp(_P_WAIT, argv[0], (const char *const *)argv); return r == 0 ? 0 : -1; } @@ -118,12 +194,12 @@ static int valid_ver(const char *s) { int digits = 0; for (const char *p = s; *p; p++) { if (*p >= '0' && *p <= '9') digits++; - else if (*p != '.') return 0; /* only digits and dots allowed */ + else if (*p != '.') return 0; } return digits > 0 && strlen(s) < 24; } static int newer(const char *remote, const char *local) { - if (!valid_ver(remote)) return 0; /* junk remote: never "newer" */ + if (!valid_ver(remote)) return 0; int ra, rb = 0, la, lb = 0; ra = atoi(remote); const char *rd = strchr(remote, '.'); if (rd) rb = atoi(rd + 1); @@ -153,6 +229,7 @@ void update_run_blocking(void) { char bp[1024]; int in_bundle = bundle_path(bp, sizeof(bp)); if (in_bundle) { + snprintf(url, sizeof(url), "%smacos-app.zip", BIN_BASE); char zip[1100]; snprintf(zip, sizeof(zip), "%s.zip", g_new); char *dz[] = { (char*)"curl", (char*)"-fsSL", (char*)"--max-time", @@ -163,9 +240,14 @@ void update_run_blocking(void) { "milna-macos-app.zip on the vps?\n"); remove(zip); return; } + char *xz[] = { (char*)"xattr", (char*)"-d", (char*)"com.apple.quarantine", zip, NULL }; run_argv(xz); + + if (fetch_and_verify(url, zip) != 0) { + remove(zip); return; + } pthread_mutex_lock(&g_mu); snprintf(g_latest, sizeof(g_latest), "%s", remote); g_ready = 1; @@ -183,8 +265,13 @@ void update_run_blocking(void) { "uploaded to the vps?\n", url); remove(g_new); return; } + + if (fetch_and_verify(url, g_new) != 0) { + remove(g_new); return; + } #ifndef _WIN32 chmod(g_new, 0755); + char *xargv[] = { (char*)"xattr", (char*)"-d", (char*)"com.apple.quarantine", g_new, NULL }; run_argv(xargv); @@ -204,6 +291,7 @@ void update_init(const char *local_version) { fprintf(stderr, "[milna] update_init: self=%s\n", g_self); #ifdef __APPLE__ + { char bp[1024]; char zip[1100]; snprintf(zip, sizeof(zip), "%s.zip", g_new); @@ -212,11 +300,13 @@ void update_init(const char *local_version) { fclose(z); fprintf(stderr, "[milna] update: staged bundle zip found, " "launching swap helper for %s\n", bp); + char hp[] = "/tmp/milna_update.XXXXXX"; int hfd = mkstemp(hp); FILE *h = (hfd >= 0) ? fdopen(hfd, "w") : NULL; if (h) { fchmod(hfd, 0700); + fprintf(h, "#!/bin/sh\n" "pid=\"$1\"; app=\"$2\"; zip=\"$3\"\n" @@ -233,7 +323,7 @@ void update_init(const char *local_version) { "rm -rf \"$app.old\" \"$tmp\" \"$zip\"\n" "/usr/bin/open \"$app\"\n"); fclose(h); - /* spawn detached and exit so the helper can replace us */ + char pidbuf[16]; snprintf(pidbuf, sizeof(pidbuf), "%d", (int)getpid()); char *hv[] = { (char*)"/bin/sh", hp, pidbuf, bp, zip, NULL }; @@ -245,7 +335,7 @@ void update_init(const char *local_version) { } fprintf(stderr, "[milna] update: helper spawned, quitting " "for bundle swap\n"); - _exit(0); /* let the helper take over */ + _exit(0); } } else if (z) fclose(z); } @@ -259,11 +349,11 @@ void update_init(const char *local_version) { #ifdef _WIN32 char old[1100]; snprintf(old, sizeof(old), "%s.old", g_self); remove(old); - MoveFileA(g_self, old); /* unlock the name */ - if (MoveFileA(g_new, g_self) == 0) /* promote */ - MoveFileA(old, g_self); /* failed: roll back */ + MoveFileA(g_self, old); + if (MoveFileA(g_new, g_self) == 0) + MoveFileA(old, g_self); else - /* relaunch into the new binary, old one exits */ + ; #else if (rename(g_new, g_self) != 0) { @@ -271,14 +361,17 @@ void update_init(const char *local_version) { "in a writable location (not /Applications)?\n", strerror(errno), g_self); } else { + fprintf(stderr, "[milna] update: promoted, relaunching\n"); #ifdef __APPLE__ + { char bundle[1024]; snprintf(bundle, sizeof(bundle), "%s", g_self); char *p = strstr(bundle, ".app/Contents/MacOS/"); if (p) { - p[4] = 0; /* terminate right after ".app" */ + p[4] = 0; + char *cs[] = { (char*)"codesign", (char*)"--force", (char*)"--sign", (char*)"-", bundle, NULL }; int rc = run_argv(cs); @@ -299,6 +392,7 @@ void update_init(const char *local_version) { } #ifdef __APPLE__ + void update_dispatch_bg(void); #endif @@ -318,7 +412,7 @@ void update_check_async(void) { } #ifndef __APPLE__ -/* pthread needs a void*(void*) signature */ + static void *update_run_blocking_thunk(void *a) { (void)a; update_run_blocking(); return NULL; }