#!/bin/sh
# mkselfcrypt.sh — pack a file or folder into a self-decrypting archive.
# Run with --help for usage.
set -eu

ITER=600000

usage() {
    cat <<'USAGEEOF'
mkselfcrypt.sh — make a password-protected, self-extracting archive.

USAGE
    sh mkselfcrypt.sh <file-or-folder> <output.sh>
    sh mkselfcrypt.sh --help

It asks for a passphrase, then writes <output.sh>: one file holding both your
encrypted contents and the code to unlock them. Open it anywhere with:

    sh <output.sh> [destination-folder]

The output needs nothing installed — only tools macOS and Linux already ship.

NOTES
    The original is NOT touched. This writes an encrypted copy; the plaintext
    is still there afterwards, and deleting it is up to you.
    Run `sh <output.sh> --help` for what the archive itself will tell you.
USAGEEOF
}

case "${1:-}" in
    -h|--help|"") usage; exit 0 ;;
esac

SRC=$1
OUT=${2:-}
[ -n "$OUT" ] || { usage >&2; exit 2; }
[ -e "$SRC" ] || { echo "no such source: $SRC" >&2; exit 1; }

TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT INT TERM

BASE=$(basename "$SRC")
DIR=$(cd "$(dirname "$SRC")" && pwd)

echo "packing $BASE ..." >&2
tar -czf "$TMP/plain.tgz" -C "$DIR" "$BASE"
SUM=$(openssl dgst -sha256 "$TMP/plain.tgz" | awk '{print $NF}')

printf 'passphrase: ' >&2
stty -echo 2>/dev/null || true
read -r PW
stty echo 2>/dev/null || true
printf '\nconfirm:    ' >&2
stty -echo 2>/dev/null || true
read -r PW2
stty echo 2>/dev/null || true
printf '\n' >&2
[ "$PW" = "$PW2" ] || { echo "passphrases differ" >&2; exit 1; }
[ -n "$PW" ] || { echo "empty passphrase refused" >&2; exit 1; }

SFX_PW=$PW; export SFX_PW
openssl enc -aes-256-cbc -md sha256 -pbkdf2 -iter "$ITER" -salt \
    -in "$TMP/plain.tgz" -out "$TMP/cipher.bin" -pass env:SFX_PW
unset SFX_PW

# The header records where it ends and the ciphertext begins. Substituting that
# number changes the header's length, so it goes in a fixed-width 16-char field
# padded on the RIGHT with a comment — the length stays put, and the number
# keeps no leading zeros (a zero-padded literal is read as OCTAL by $(( )) ).
cat >"$TMP/header" <<HEADEREOF
#!/bin/sh
# ENCRYPTED ARCHIVE — run \`sh \$(basename \$0) --help\` for what this is.
set -eu
OFF=__OFFSET_FIELD__
SUM=$SUM
ITER=$ITER

ME=\$(basename "\$0")
case "\${1:-}" in
    -h|--help)
        cat <<HELPEOF
\$ME — a password-protected archive.

This file is not a document. It holds encrypted contents plus the code needed
to unlock them, so it opens on any Mac or Linux machine with nothing installed.

YOU NEED THE PASSPHRASE. Without it the contents cannot be recovered — there is
no reset and no recovery. Whoever made this file has it.

USAGE
    sh \$ME                      unlock into the current folder
    sh \$ME <destination>        unlock into that folder

You will be prompted for the passphrase; it will not echo as you type.
The archive is left intact — unlocking writes a decrypted copy elsewhere.

AES-256-CBC, PBKDF2-SHA256, \$ITER iterations.

If macOS refuses to run this after a download or AirDrop:
    xattr -d com.apple.quarantine \$ME
HELPEOF
        exit 0 ;;
esac

SELF=\$0
case \$SELF in /*) ;; *) SELF=\$PWD/\$SELF ;; esac

command -v openssl >/dev/null 2>&1 || { echo "openssl not found" >&2; exit 1; }

DEST=\${1:-.}
[ -d "\$DEST" ] || { echo "not a directory: \$DEST" >&2; exit 1; }

TMP=\$(mktemp -d)
trap 'rm -rf "\$TMP"' EXIT INT TERM

printf 'passphrase: ' >&2
stty -echo 2>/dev/null || true
read -r PW
stty echo 2>/dev/null || true
printf '\n' >&2

SFX_PW=\$PW; export SFX_PW
if ! tail -c +\$((OFF + 1)) "\$SELF" | openssl enc -d -aes-256-cbc -md sha256 \\
        -pbkdf2 -iter "\$ITER" -pass env:SFX_PW >"\$TMP/plain.tgz" 2>/dev/null; then
    unset SFX_PW
    echo "decryption failed — wrong passphrase, or the file is damaged" >&2
    exit 1
fi
unset SFX_PW

GOT=\$(openssl dgst -sha256 "\$TMP/plain.tgz" | awk '{print \$NF}')
[ "\$GOT" = "\$SUM" ] || { echo "checksum mismatch — archive is corrupt" >&2; exit 1; }

tar -xzf "\$TMP/plain.tgz" -C "\$DEST"
echo "unlocked into \$DEST" >&2
exit 0
HEADEREOF

LEN=$(wc -c <"$TMP/header" | tr -d ' ')
FIELD=$(awk -v n="$LEN" 'BEGIN{ s = n " #"; while (length(s) < 16) s = s "#"; print s }')
[ "${#FIELD}" -eq 16 ] || { echo "internal: offset too large to encode" >&2; exit 1; }
sed "s/__OFFSET_FIELD__/$FIELD/" "$TMP/header" >"$TMP/header.final"

LEN2=$(wc -c <"$TMP/header.final" | tr -d ' ')
[ "$LEN" = "$LEN2" ] || { echo "internal: header length drifted" >&2; exit 1; }

cat "$TMP/header.final" "$TMP/cipher.bin" >"$OUT"
# Deliberately NOT chmod +x: the output is meant to be run as `sh <file>`.
# Making it executable here also matches a macOS XProtect malware signature
# for droppers that assemble an executable at runtime — which gets THIS
# script silently deleted by the OS.

echo "wrote $OUT ($(wc -c <"$OUT" | tr -d ' ') bytes)" >&2
echo "open it with:  sh $OUT [destination]   (--help for details)" >&2
