#!/usr/bin/env bash # e2e_test.sh — the obfuscator use case, end to end against b3u.dev. # # Source: authored in b3u_use_cases (the compendium's section 1.2 # build-and-run flow, made executable). # # Downloads the platform zip, lays out ~/ccsUseCases/obfuscator, # builds the obfuscator against the delivered SCB C++ library, and # then RUNS THE TWO ORACLES the compendium describes: # # algorithm 0 no-op round trip -> output must be BYTE-IDENTICAL # to the input. This is a structural sanity check that # SCB <-> SCR conversion is lossless on your grammar. # algorithm 1 cipher then decipher -> must restore the original # byte-for-byte, AND the ciphered form must differ # from it. A cipher that round-trips but changes # nothing is the failure this catches. # # Unlike the other use cases this one does not stop at a built # executable: the oracles are cheap, they are the whole point of # algorithm 0, and a platform you are about to write algorithms on # should prove itself on YOUR grammar before you start. # # PRECONDITIONS # - a bundles zip already unpacked, providing scbcpp/ (any unit — # the obfuscator is grammar-agnostic; run ccs_json/e2e_test.sh # first if you have no layout yet) # - a grammar binary (.fgr) from your own compile # # Environment (same surface as the other use-case scripts): # B3U_BASE, B3U_EMAIL, B3U_PASSWORD required # B3U_DOWNLOADS default ~/Downloads # CCSUSECASES_ROOT default ~/ccsUseCases # OBF_SCBCPP default /ccs_json/scbcpp # OBF_FGR default /ccs_json/json.sgr.fgr # CF_ACCESS_CLIENT_ID/-_SECRET optional gateway headers set -u : "${B3U_BASE:?set B3U_BASE (e.g. https://b3u.dev)}" : "${B3U_EMAIL:?set B3U_EMAIL}" : "${B3U_PASSWORD:?set B3U_PASSWORD}" DOWNLOADS="${B3U_DOWNLOADS:-$HOME/Downloads}" ROOT_DIR="${CCSUSECASES_ROOT:-$HOME/ccsUseCases}" SCBCPP="${OBF_SCBCPP:-$ROOT_DIR/ccs_json/scbcpp}" FGR="${OBF_FGR:-$ROOT_DIR/ccs_json/json.sgr.fgr}" WORK="$ROOT_DIR/obfuscator" CURL=(curl -s) if [ -n "${CF_ACCESS_CLIENT_ID:-}" ]; then CURL+=(-H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" -H "CF-Access-Client-Secret: ${CF_ACCESS_CLIENT_SECRET:?}") fi fails=0 pass() { echo " PASS $1"; } fail() { echo " FAIL $1"; fails=$((fails+1)); } mkdir -p "$DOWNLOADS" echo "== 1. login ==" WALL_ALTERNATIVES=" Compile the grammar with a local cppcc if you have one licensed -- this archive ships the .sgr, which is the artifact the service would compile for you." # ⚠ THE LOGIN MUST DIAGNOSE, NOT TRACEBACK. This piped curl straight # into json.load, so ANY non-JSON reply -- a Cloudflare Access redirect, # a 502, a captive portal -- surfaced as a raw Python JSONDecodeError # followed by "check B3U_EMAIL/B3U_PASSWORD", which was both wrong and # unactionable. Found by a user whose credentials were perfectly fine # and who was told to check them anyway. _lb=$(mktemp); _lh=$(mktemp) code=$("${CURL[@]}" -o "$_lb" -D "$_lh" -w '%{http_code}' \ -X POST "$B3U_BASE/login" -H 'Content-Type: application/json' \ -d "{\"email\":\"$B3U_EMAIL\",\"password\":\"$B3U_PASSWORD\"}") token=$(python3 -c 'import json,sys try: print(json.load(open(sys.argv[1])).get("session_token","")) except Exception: print("")' "$_lb") if [ -n "$token" ]; then pass "login -> bearer token" else if grep -qi 'cloudflareaccess\.com' "$_lh" "$_lb" 2>/dev/null; then fail "login -- $B3U_BASE is behind a Cloudflare Access wall (HTTP $code)" echo " The wall answers BEFORE the application does, so no" echo " email or password can reach it. Your credentials are" echo " not the problem." echo echo " b3u.dev itself is open -- so a wall in front of it means" echo " B3U_BASE is pointed at a PRIVATE host (a staging or" echo " preview hostname), not at the public service. Check" echo " B3U_BASE; it should be https://b3u.dev." if [ -n "${WALL_ALTERNATIVES:-}" ]; then echo "$WALL_ALTERNATIVES" fi # (CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET are honoured above # if you hold a service token for a private host. Reaching the # public service needs no token.) elif [ "$code" = 401 ] || [ "$code" = 403 ]; then fail "login -- rejected (HTTP $code). Check B3U_EMAIL / B3U_PASSWORD." elif [ "$code" = 000 ]; then fail "login -- could not reach $B3U_BASE at all (DNS, TLS or network)." else fail "login -- no session_token in the reply (HTTP $code)" echo " first bytes: $(head -c 160 "$_lb" | tr -d '\r\n')" fi rm -f "$_lb" "$_lh"; exit 1 fi rm -f "$_lb" "$_lh" AUTH=(-H "Authorization: Bearer $token") echo "== 2. download the platform zip ==" code=$("${CURL[@]}" "${AUTH[@]}" \ -o "$DOWNLOADS/ccs_obfuscator-platform.zip" -w '%{http_code}' \ "$B3U_BASE/usecases/ccs_obfuscator-platform.zip") [ "$code" = 200 ] && pass "GET /usecases/ccs_obfuscator-platform.zip" \ || { fail "platform zip download (got $code)"; exit 1; } echo "== 3. lay out the working directory ==" mkdir -p "$WORK" ( cd "$WORK" && unzip -oq "$DOWNLOADS/ccs_obfuscator-platform.zip" ) missing="" for m in obfuscator.cc cipher.h xtea.h makefile; do [ -f "$WORK/$m" ] || missing="$missing $m" done [ -z "$missing" ] && pass "platform unpacked (obfuscator.cc, cipher.h, xtea.h, makefile)" \ || { fail "missing from the zip:$missing"; exit 1; } echo "== 4. preconditions ==" [ -f "$SCBCPP/lib/libscbcpp.a" ] && pass "SCB C++ library present ($SCBCPP)" \ || { fail "no libscbcpp.a under $SCBCPP — unpack a bundles zip first, or set OBF_SCBCPP"; exit 1; } [ -f "$FGR" ] && pass "grammar binary present ($(basename "$FGR"))" \ || { fail "no .fgr at $FGR — compile a grammar first, or set OBF_FGR"; exit 1; } echo "== 5. build ==" ( cd "$WORK" && make SCBCPP="$SCBCPP" ) > "$WORK/make.log" 2>&1 [ -x "$WORK/obfuscator" ] && pass "make (see make.log)" \ || { fail "build failed — see $WORK/make.log"; tail -15 "$WORK/make.log"; exit 1; } echo "== 6. ORACLE: algorithm 0 is lossless ==" base="$(basename "$FGR")" cp "$FGR" "$WORK/$base" ( cd "$WORK" && ./obfuscator -a 0 "$base" ) > "$WORK/algo0.log" 2>&1 out0="$WORK/${base%.fgr}.obf.0.fgr" if [ -f "$out0" ] && cmp -s "$WORK/$base" "$out0"; then pass "no-op round trip is BYTE-IDENTICAL (SCB <-> SCR is lossless on your grammar)" else fail "algorithm 0 did not reproduce the input — see $WORK/algo0.log" fi echo "== 7. ORACLE: algorithm 1 ciphers and restores ==" ( cd "$WORK" && ./obfuscator -a 1 -k e2e-demo-seed "$base" ) > "$WORK/algo1.log" 2>&1 enc="$WORK/${base%.fgr}.obf.1.fgr" if [ -f "$enc" ] && ! cmp -s "$WORK/$base" "$enc"; then pass "ciphered form DIFFERS from the input" else fail "algorithm 1 produced no change — see $WORK/algo1.log" fi cp "$enc" "$WORK/restore.fgr" 2>/dev/null ( cd "$WORK" && ./obfuscator -d 1 -k e2e-demo-seed restore.fgr ) >> "$WORK/algo1.log" 2>&1 # the inverse writes .de.fgr (NOT .obf.1.fgr — the naming caught # us once while proving this script, so it is pinned here explicitly) if [ -f "$WORK/restore.de.fgr" ] && cmp -s "$WORK/$base" "$WORK/restore.de.fgr"; then pass "decipher restores the original BYTE-FOR-BYTE" else fail "algorithm 1 did not round-trip — see $WORK/algo1.log" fi echo if [ "$fails" -eq 0 ]; then echo "CCS_OBFUSCATOR E2E: ALL PASS ($WORK/obfuscator is ready;" echo " section 1.7 of the compendium is the cookbook for adding" echo " your own algorithm, and section 6 is why it is worth doing)" else echo "CCS_OBFUSCATOR E2E: $fails FAIL" fi exit "$fails"