#!/usr/bin/env bash
# Runs inside the pb-lab container (see run.sh). Every step from the post, in order.
set -u
cd /lab
step() { printf '\n========== %s ==========\n' "$*"; }
rc() { echo "exit=$1"; }

step "1. emit + verify"
proofbundle emit --payload-file result.json --new-key signer.key --out receipt.json
proofbundle verify receipt.json; rc $?
cat receipt.json

step "2. check by hand (no proofbundle code)"
python check_by_hand.py receipt.json

step "3a. edit result.json on disk, verify the old receipt"
python -c "import json;p=json.load(open('result.json'));p['score']=0.11;json.dump(p,open('result_tampered.json','w'),indent=2)"
proofbundle verify receipt.json >/dev/null; rc $?
for f in result.json result_tampered.json; do
  python -c "import json,base64,hashlib,sys;r=json.load(open('receipt.json'));a=hashlib.sha256(base64.b64decode(r['payload_b64'])).hexdigest();b=hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest();print(sys.argv[1].ljust(20),'MATCH' if a==b else 'MISMATCH','receipt',a[:16],'file',b[:16])" "$f"
done

step "3b. the generic receipt tamper from the challenge"
python tamper_generic.py
diff <(python -m json.tool receipt.json) <(python -m json.tool receipt_tampered.json)
proofbundle verify receipt_tampered.json; rc $?

step "3c. edit the score inside the signed payload"
python tamper_payload.py
proofbundle verify receipt_payload_edit.json | head -3; rc ${PIPESTATUS[0]}

step "4. forgery: re-sign a fake result with a new key"
python -c "import json;p=json.load(open('result.json'));p['score']=0.11;json.dump(p,open('result_forged.json','w'),indent=2)"
proofbundle emit --payload-file result_forged.json --new-key attacker.key --out receipt_forged.json
proofbundle verify receipt_forged.json | head -3; rc ${PIPESTATUS[0]}
python check_by_hand.py receipt_forged.json

step "5. pin the signer with a trust policy"
python make_policy.py receipt.json
proofbundle policy explain trust_policy.json
proofbundle policy lint --strict trust_policy.json; rc $?
echo "--- genuine"; proofbundle verify receipt.json --policy trust_policy.json | grep -E '^(CRYPTO|POLICY)'; rc ${PIPESTATUS[0]}
echo "--- forged";  proofbundle verify receipt_forged.json --policy trust_policy.json | grep -E '^(CRYPTO|POLICY)'; rc ${PIPESTATUS[0]}
python -c "import json;p=json.load(open('trust_policy.json'));p['alowed_issuers']=p.pop('allowed_issuers');json.dump(p,open('trust_policy_typo.json','w'))"
echo "--- typo in policy"; proofbundle verify receipt.json --policy trust_policy_typo.json; rc $?

step "6. stolen key"
python -c "import json;p=json.load(open('result.json'));p['score']=0.42;json.dump(p,open('result_stolen.json','w'),indent=2)"
proofbundle emit --payload-file result_stolen.json --key signer.key --out receipt_stolen.json
proofbundle verify receipt_stolen.json --policy trust_policy.json | grep -E '^(CRYPTO|POLICY)'; rc ${PIPESTATUS[0]}
python -c "import json;p=json.load(open('trust_policy.json'));p['status']={'reject_self_issued':True,'allowed_status_authorities':[]};json.dump(p,open('trust_policy_status.json','w'),indent=2)"
echo "--- policy that asks for revocation checking"
proofbundle verify receipt.json --policy trust_policy_status.json | grep -E '^POLICY'; rc ${PIPESTATUS[0]}

echo "--- rotate: drop the stolen key, pin a new one"
python -c "import os;open('signer2.key','wb').write(os.urandom(32))"
proofbundle emit --payload-file result.json --key signer2.key --out receipt_v2.json >/dev/null
cp trust_policy.json trust_policy_old.json
python make_policy.py receipt_v2.json >/dev/null && mv trust_policy.json trust_policy_rotated.json && mv trust_policy_old.json trust_policy.json
for r in receipt.json receipt_stolen.json receipt_v2.json; do printf '%-20s ' $r; proofbundle verify $r --policy trust_policy_rotated.json | grep '^POLICY'; done

step "7. serialization"
python -c "import json;r=json.load(open('receipt.json'));json.dump(dict(sorted(r.items(),reverse=True)),open('receipt_min.json','w'),separators=(',',':'))"
echo "--- receipt minified + keys reordered"; proofbundle verify receipt_min.json | grep '^CRYPTO'; rc ${PIPESTATUS[0]}
python -c "import json;json.dump(json.load(open('result.json')),open('result_min.json','w'),separators=(',',':'))"
python -c "import json,base64;p=base64.b64decode(json.load(open('receipt.json'))['payload_b64']);m=open('result_min.json','rb').read();print('same bytes:',p==m,'| same JSON value:',json.loads(p)==json.loads(m))"
python - <<'PY'
raw = open("receipt.json").read()
open("receipt_dupkey.json", "w").write(raw.replace('{\n  "schema"', '{\n  "payload_b64": "AAAA",\n  "schema"', 1))
PY
echo "--- duplicate key"; proofbundle verify receipt_dupkey.json; rc $?

step "8. eval receipts"
python make_claim.py self_attested
proofbundle emit-eval --claim claim_self_attested.json --key signer.key --out eval_receipt.json
proofbundle show-eval eval_receipt.json; rc $?
python make_claim.py reproduced >/dev/null
proofbundle emit-eval --claim claim_reproduced.json --key signer.key --out eval_receipt_reproduced.json
proofbundle show-eval eval_receipt_reproduced.json | grep -E '^(assurance|WARNING|=>)'
python make_policy.py receipt.json reproduced
for r in eval_receipt.json eval_receipt_reproduced.json; do
  echo "--- $r"; proofbundle verify $r --policy trust_policy_reproduced.json | grep -E '^(CRYPTO|POLICY|ASSURANCE)'; rc ${PIPESTATUS[0]}
done

step "9. volume: 300 receipts"
mkdir -p bulk
t0=$(date +%s.%N)
for i in $(seq 1 300); do printf '{"run":%d,"score":0.9}\n' $i > bulk/r$i.json; proofbundle emit --payload-file bulk/r$i.json --key signer.key --out bulk/receipt_$i.json >/dev/null; done
t1=$(date +%s.%N)
fails=0; for f in bulk/receipt_*.json; do proofbundle verify $f --policy trust_policy.json >/dev/null || fails=$((fails+1)); done
t2=$(date +%s.%N)
python -c "print(f'emit 300: {$t1-$t0:.1f}s   verify 300: {$t2-$t1:.1f}s   failures: $fails')"
python -c "import json,glob;rs=[json.load(open(f)) for f in glob.glob('bulk/receipt_*.json')];print('receipts:',len(rs),'| distinct Merkle roots:',len({r['merkle']['root_b64'] for r in rs}),'| tree sizes:',sorted({r['merkle']['tree_size'] for r in rs}))"
