Metadata-Version: 2.4
Name: human-attestation
Version: 0.4.2
Summary: Official SDK for HAP (Human Attestation Protocol) - cryptographic proof of verified human effort
Project-URL: Homepage, https://github.com/Blue-Scroll/hap
Project-URL: Repository, https://github.com/Blue-Scroll/hap.git
Project-URL: Documentation, https://github.com/Blue-Scroll/hap#readme
Author: BlueScroll Inc.
License-Expression: Apache-2.0
Keywords: attestation,cryptographic,ed25519,hap,human-attestation-protocol,jws,proof-of-effort,sender-verification,verification
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.9
Requires-Dist: cryptography>=41.0.0
Requires-Dist: httpx>=0.25.0
Requires-Dist: pyjwt>=2.8.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# human-attestation

Official HAP (Human Attestation Protocol) SDK for Python.

HAP is an open standard for verified human effort. It enables Verification Authorities (VAs) to cryptographically attest that a sender took deliberate, costly action when communicating with a recipient.

## Installation

```bash
pip install human-attestation
```

## Quick Start

### Verifying a Claim (For Recipients)

```python
import asyncio
from human_attestation import verify_claim, is_claim_expired, is_claim_for_recipient

async def main():
    # Verify a claim from a HAP ID
    claim = await verify_claim("hap_abc123xyz456", "ballista.jobs")

    if claim:
        # Check if not expired
        if is_claim_expired(claim):
            print("Claim has expired")
            return

        # Verify it's for your organization
        if not is_claim_for_recipient(claim, "yourcompany.com"):
            print("Claim is for a different recipient")
            return

        print(f"Verified {claim['method']} application to {claim['to']['name']}")

asyncio.run(main())
```

### Verifying from a URL

```python
from human_attestation import extract_id_from_url, verify_claim

async def verify_from_url(url: str):
    # Extract HAP ID from a verification URL
    hap_id = extract_id_from_url(url)

    if hap_id:
        claim = await verify_claim(hap_id, "ballista.jobs")
        return claim
    return None
```

### Verifying Signature Manually

```python
from human_attestation import fetch_claim, verify_signature

async def verify_with_signature(hap_id: str):
    # Fetch the claim
    response = await fetch_claim(hap_id, "ballista.jobs")

    if response.get("valid") and "jws" in response:
        # Verify the cryptographic signature
        result = await verify_signature(response["jws"], "ballista.jobs")

        if result["valid"]:
            print("Signature verified!", result["claim"])
        else:
            print("Signature invalid:", result["error"])
```

### Signing Claims (For Verification Authorities)

```python
import json
from human_attestation import (
    generate_key_pair,
    export_public_key_jwk,
    create_claim,
    sign_claim,
)

# Generate a key pair (do this once, store securely)
private_key, public_key = generate_key_pair()

# Export public key for /.well-known/hap.json
jwk = export_public_key_jwk(public_key, "my_key_001")
well_known = {"issuer": "my-va.com", "keys": [jwk]}
print(json.dumps(well_known, indent=2))

# Create and sign a claim
claim = create_claim(
    method="physical_mail",
    description="Priority mail packet with handwritten cover letter",
    recipient_name="Acme Corp",
    domain="acme.com",
    issuer="my-va.com",
    expires_in_days=730,  # 2 years
    cost={"amount": 1500, "currency": "USD"},
    time=1800,
    physical=True,
)

jws = sign_claim(claim, private_key, kid="my_key_001")
print("Signed JWS:", jws)
```

## API Reference

### Verification Functions

| Function                                | Description                                     |
| --------------------------------------- | ----------------------------------------------- |
| `verify_claim(hap_id, issuer)`          | Fetch and verify a claim, returns claim or None |
| `fetch_claim(hap_id, issuer)`           | Fetch raw verification response from VA         |
| `verify_signature(jws, issuer)`         | Verify JWS signature against VA's public keys   |
| `fetch_public_keys(issuer)`             | Fetch VA's public keys from well-known endpoint |
| `is_valid_id(id)`                       | Check if string matches HAP ID format           |
| `extract_id_from_url(url)`              | Extract HAP ID from verification URL            |
| `is_claim_expired(claim)`               | Check if claim has passed expiration            |
| `is_claim_for_recipient(claim, domain)` | Check if claim targets specific recipient       |

### Signing Functions (For VAs)

| Function                              | Description                              |
| ------------------------------------- | ---------------------------------------- |
| `generate_key_pair()`                 | Generate Ed25519 key pair                |
| `export_public_key_jwk(key, kid)`     | Export public key as JWK                 |
| `sign_claim(claim, private_key, kid)` | Sign a claim, returns JWS                |
| `generate_id()`                       | Generate cryptographically secure HAP ID |
| `create_claim(...)`                   | Create claim with defaults               |

### Types

```python
from human_attestation import (
    Claim,
    ClaimCost,
    ClaimTarget,
    VerificationResponse,
    WellKnown,
    Jwk,
)
```

## Requirements

- Python 3.9+
- httpx (for async HTTP)
- PyJWT with cryptography

## License

Apache-2.0
