#!/usr/bin/env bash
# Offline decrypt of an MDIP encrypted message with stock OpenSSL (no npm, no network).
# MDIP (@mdip/cipher 2.2.0 encryptMessage) wire format, as implemented by the kit's dependency:
#   S      = secp256k1 ECDH(receiver priv, sender pub), as a COMPRESSED point (33 bytes: 02|03 || X)
#   key    = S[0:32]  (the parity byte + the first 31 bytes of X; OpenSSL -derive gives X only,
#            so we try prefix 02 and 03 and keep the one whose Poly1305 tag verifies)
#   blob   = base64url-decode(cipher_receiver) = nonce(24) || ciphertext || tag(16)
#   cipher = XChaCha20-Poly1305(key, nonce), no AAD.  XChaCha = HChaCha20 subkey + IETF ChaCha20-Poly1305
#            with nonce 00000000||nonce[16:24]. HChaCha20 is recovered from one raw ChaCha20 block
#            (keystream minus the initial state words 0-3 and 12-15).
# Usage: decrypt-openssl.sh <receiver-priv.pem> <sender-pub.pem> <blob.bin>   -> plaintext on stdout
# Exit 0 only if the Poly1305 tag verifies.
set -euo pipefail
priv=$1 pub=$2 blob=$3
hex()   { od -An -v -tx1 | tr -d ' \n'; }
unhex() { printf "$(printf '%s' "$1" | sed 's/../\\x&/g')"; }
le32()  { local h=$1; echo $((16#${h:6:2}${h:4:2}${h:2:2}${h:0:2})); }
tole32(){ printf '%02x%02x%02x%02x' $(($1&255)) $((($1>>8)&255)) $((($1>>16)&255)) $((($1>>24)&255)); }
le64()  { local n=$1 o= i; for i in 0 1 2 3 4 5 6 7; do o+=$(printf '%02x' $(((n>>(8*i))&255))); done; echo "$o"; }
ks()    { head -c "$3" /dev/zero | openssl enc -chacha20 -K "$1" -iv "$2" | hex; }   # raw keystream

B=$(hex < "$blob"); N=${B:0:48}; CT=${B:48:$((${#B}-48-32))}; TAG=${B: -32}
X=$(openssl pkeyutl -derive -inkey "$priv" -peerkey "$pub" | hex)
tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
unhex "$CT" > "$tmp/ct"
for prefix in 02 03; do
  key=$prefix${X:0:62}
  # HChaCha20(key, nonce[0:16])
  K=$(ks "$key" "${N:0:32}" 64); sub=
  sigma=(1634760805 857760878 2036477234 1797285236)
  for i in 0 1 2 3; do sub+=$(tole32 $(( ($(le32 ${K:$((8*i)):8}) - ${sigma[$i]}) & 0xffffffff ))); done
  for i in 0 1 2 3; do sub+=$(tole32 $(( ($(le32 ${K:$((8*(12+i))):8}) - $(le32 ${N:$((8*i)):8})) & 0xffffffff ))); done
  n12=00000000${N:32:16}
  polykey=$(ks "$sub" "00000000$n12" 32)
  pad=$(( (16 - ${#CT}/2 % 16) % 16 ))
  { cat "$tmp/ct"; head -c $pad /dev/zero; unhex "$(le64 0)$(le64 $((${#CT}/2)))"; } > "$tmp/macdata"
  t=$(openssl mac -macopt hexkey:"$polykey" -in "$tmp/macdata" POLY1305 | tr 'A-F' 'a-f')
  if [ "$t" = "$TAG" ]; then
    openssl enc -chacha20 -K "$sub" -iv "01000000$n12" < "$tmp/ct"
    exit 0
  fi
done
echo "decrypt-openssl.sh: Poly1305 tag did not verify (wrong key or tampered ciphertext)" >&2
exit 1
