# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-a-broken-attestation-chain-when-an-intermediate-agent-re-signs-a-delegated-token-with-its-own-key.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-a-broken-attestation-chain-when-an-intermediate-agent-re-signs-a-delegated-token-with-its-own-key.warrant.json
# Markdown SHA-256:   6032e1c8e8c53933b5914b8d48b54011b7fd4202bd5721005d4c1f227a791481
# Code AST SHA-256:   3aedf16f4a6c25e86cdc978fff365aac7da9d39bdb457696fe8bd2fe89d25921
# Verified Classes:   AttestationChainError
# Defensive Guards:   5 fail-closed invariants (zero external deps)

class AttestationChainError(RuntimeError):
    """Raised when a delegation chain is signature-valid but structurally incomplete."""
    pass


def verify_transitive_chain(token, trusted_roots, parent_scope_by_hop):
    """
    Verify a delegated token under a transitive-trust model.

    Rejects signature-valid tokens that omit the chain to a trusted root
    (the re-signing failure mode), and rejects scope amplification at any hop.
    """
    chain = token.get("delegation_chain")
    if not chain:
        # Signature may be valid; the chain is not. Hard fail under transitive trust.
        raise AttestationChainError(
            f"token from issuer {token.get('iss')!r} carries no delegation_chain; "
            "cannot establish transitive trust to a root principal. "
            "This is the re-signing failure mode, not a valid single-hop token."
        )

    root = chain[0].get("issuer")
    if root not in trusted_roots:
        raise AttestationChainError(
            f"chain root {root!r} is not a trusted anchor; "
            f"trusted roots are {sorted(trusted_roots)!r}"
        )

    for i, hop in enumerate(chain):
        if not _signature_valid(hop):
            raise AttestationChainError(
                f"hop {i} (issuer={hop.get('issuer')!r}) failed signature verification"
            )
        if i > 0:
            parent = chain[i - 1]
            if hop.get("parent_hash") != _hash(parent):
                raise AttestationChainError(
                    f"hop {i} parent_hash does not match hop {i-1}; chain is not hash-linked"
                )
            allowed = set(parent.get("may_delegate", []))
            carried = set(hop.get("scope", []))
            if not carried <= allowed:
                raise AttestationChainError(
                    f"hop {i} (issuer={hop.get('issuer')!r}) carries scope "
                    f"{sorted(carried - allowed)!r} outside parent's may_delegate set "
                    f"{sorted(allowed)!r}; scope amplification rejected"
                )
    return chain[-1]


def _signature_valid(hop):
    return bool(hop.get("sig"))


def _hash(hop):
    import hashlib, json
    return hashlib.sha256(
        json.dumps(hop, sort_keys=True).encode()
    ).hexdigest()
