Metadata-Version: 2.4
Name: certisigma
Version: 1.5.0
Summary: Official Python SDK for the CertiSigma attestation & verification API
Author: CertiSigma
License: MIT
Project-URL: Homepage, https://developers.certisigma.ch/sdk
Project-URL: Documentation, https://developers.certisigma.ch/sdk
Project-URL: API Reference, https://developers.certisigma.ch
Project-URL: Repository, https://github.com/massimocavallin/CertiSigma
Project-URL: Changelog, https://developers.certisigma.ch/sdk#changelog
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security :: Cryptography
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.25.0
Provides-Extra: crypto
Requires-Dist: cryptography>=44.0.0; extra == "crypto"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: respx>=0.21; extra == "dev"
Requires-Dist: cryptography>=44.0.0; extra == "dev"

# CertiSigma Python SDK

Official Python client for the [CertiSigma](https://developers.certisigma.ch/sdk) cryptographic attestation [API](https://developers.certisigma.ch).

[![PyPI](https://img.shields.io/pypi/v/certisigma)](https://pypi.org/project/certisigma/)
[![npm](https://img.shields.io/npm/v/@certisigma/sdk)](https://www.npmjs.com/package/@certisigma/sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

## Installation

```bash
pip install certisigma
```

Or from source:

```bash
cd sdk/python
pip install -e .
```

## Quick Start

```python
import os
from certisigma import CertiSigmaClient, hash_file, hash_bytes

client = CertiSigmaClient(api_key=os.environ["CERTISIGMA_API_KEY"])

# 1. Compute the SHA-256 hash of your file
file_hash = hash_file("contract.pdf")

# 2. Attest — creates a timestamped, signed proof of existence
result = client.attest(file_hash, source="my-app")
print(f"Attestation: {result.id} at {result.timestamp}")
print(f"ECDSA signature: {result.signature}")

# 3. Verify — confirm the hash was attested
check = client.verify(file_hash)
print(f"Exists: {check.exists}, Level: {check.level}")

# Or hash raw bytes
data_hash = hash_bytes(b"any raw content")
result = client.attest(data_hash)
```

## Public Verification (No API Key)

Verification endpoints are public. You can verify attestations without any API key:

```python
from certisigma import CertiSigmaClient, hash_file

# No api_key needed — works out of the box
client = CertiSigmaClient()

file_hash = hash_file("contract.pdf")
check = client.verify(file_hash)
print(f"Exists: {check.exists}, Level: {check.level}")

# Batch verify also works without a key
results = client.batch_verify([file_hash])
print(f"Found: {results.found}/{results.count}")
```

## Hashing Utilities

Standalone SHA-256 hash functions -- compute hashes without attestation:

```python
from certisigma import hash_file, hash_bytes

# Hash a file (streamed, constant memory)
file_hash = hash_file("/path/to/document.pdf")
print(f"SHA-256: {file_hash}")

# Hash raw bytes
data_hash = hash_bytes(b"raw content")

# Verify a file against a known hash
assert hash_file("/path/to/document.pdf") == file_hash
```

Or hash + attest in one step:

```python
result = client.attest_file("/path/to/document.pdf")
print(f"Attested: {result.hash_hex}")
```

## Async Support

```python
import asyncio, os
from certisigma import AsyncCertiSigmaClient

async def main():
    async with AsyncCertiSigmaClient(api_key=os.environ["CERTISIGMA_API_KEY"]) as client:
        result = await client.attest("abcdef..." * 4 + "0" * 16)
        print(result.id)

asyncio.run(main())
```

## Batch Operations

```python
# Attest up to 100 hashes in one call — returns full claim metadata per item
batch = client.batch_attest(
    ["aabb..." * 4, "ccdd..." * 4],
    source="monthly-invoices"
)
print(f"Created: {batch.created}, Existing: {batch.existing}")
for att in batch.attestations:
    print(f"  {att['id']} claim={att['claim_id']} src={att['source']}")

# Verify batch (public, no key needed)
results = client.batch_verify(["aabb..." * 4, "ccdd..." * 4])
print(f"Found: {results.found}/{results.count}")

# Detailed mode — certification level + claim metadata (requires api_key)
results = client.batch_verify(["aabb..." * 4], detailed=True)
for r in results.results:
    if r["exists"]:
        print(f"  {r['id']} level={r['level']} src={r['source']}")
```

## Attestation Status

Check the current trust tier of an attestation (public, no API key needed):

```python
status = client.status("att_1234")
print(f"Level: {status.level}")  # "T0", "T1", or "T2"
print(f"Signature: {status.signature_available}")
print(f"Merkle: {status.merkle_proof_available}")
print(f"OTS: {status.ots_available}")
```

| Method | Input | Auth | Best for |
|--------|-------|------|----------|
| `verify(hash)` | SHA-256 hash | Optional | Compliance checks, "does this hash exist?" |
| `status(att_id)` | Attestation ID | No | Dashboards, progress tracking, polling |
| `get_evidence(att_id)` | Attestation ID | No | Independent verification, long-term archival |

## Metadata Management

```python
# Update claim metadata
result = client.update_metadata("att_1234", source="pipeline-v2", extra_data={"project": "alpha"})

# Soft-delete claim
client.delete_metadata("att_1234")

# Get evidence
evidence = client.get_evidence("att_1234")
print(evidence.level, evidence.t0)
```

## Evidence & OTS Verification

```python
import os
from certisigma import CertiSigmaClient, get_blockchain_url, save_ots_proof

client = CertiSigmaClient(api_key=os.environ["CERTISIGMA_API_KEY"])

# Get full cryptographic evidence (T0 + T1 + T2)
evidence = client.get_evidence("att_1234")

if evidence.level == "T2":
    # Bitcoin block explorer link
    print(get_blockchain_url(evidence))        # mempool.space/block/...
    print(get_blockchain_url(evidence, "tx"))   # mempool.space/tx/...

    # Save the raw .ots proof for independent verification
    save_ots_proof(evidence, "contract.pdf.ots")
    # Then verify with: ots verify contract.pdf.ots
```

## Client-Side Encryption (Zero Knowledge)

Requires: `pip install certisigma[crypto]`

```python
from certisigma.crypto import generate_key, encrypt_metadata, decrypt_metadata

# Generate a key (store securely — server never sees it)
key = generate_key()

# Encrypt before sending
encrypted = encrypt_metadata({"secret": "classified"}, key)
result = client.attest(hash_hex, extra_data=encrypted, client_encrypted=True)

# Decrypt after retrieving
plaintext = decrypt_metadata(result.extra_data, key)
```

## Error Handling

```python
from certisigma import (
    CertiSigmaError,
    AuthenticationError,
    RateLimitError,
    QuotaExceededError,
)

try:
    client.attest(hash_hex)
except AuthenticationError:
    print("Invalid API key")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")
except QuotaExceededError:
    print("Monthly quota reached")
except CertiSigmaError as e:
    print(f"API error {e.status_code}: {e}")
```

## Configuration

| Parameter | Default | Description |
|-----------|---------|-------------|
| `api_key` | `None` | Bearer token (`cs_live_...`). Optional for verify/health. |
| `base_url` | `https://api.certisigma.ch` | API endpoint |
| `timeout` | `30.0` | Request timeout in seconds |

**Proxy:** `httpx` respects `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` environment variables.
**Custom TLS CA:** Configure `httpx` with `verify="/path/to/ca-bundle.pem"` for corporate proxies.
**Retry:** Not built-in. Implement at caller level — retry only on `429` and `5xx`, fail fast on other `4xx`.

## Security & Privacy

CertiSigma is a public attestation platform. Cryptographic proofs (hash, signature, Merkle, OTS) are intentionally public and verifiable by any party. Organizational metadata (`source`, `extra_data`) is never exposed on public endpoints — only the authenticated API key owner can read their own claim data. For sensitive metadata, use client-side encryption (`encrypt_metadata()`).

See the [Security & Privacy Model](https://developers.certisigma.ch/sdk#security--privacy-model) in the full documentation for the complete threat model and data boundary.

## Test Vectors

Canonical test vectors with real production data are available for independent T0/T1/T2 verification:

- [Test Vectors documentation](https://developers.certisigma.ch/sdk#test-vectors)
- [Machine-readable JSON](https://developers.certisigma.ch/test-vectors.json)

## Compatibility

- Follows [Semantic Versioning 2.0.0](https://semver.org/).
- SDK v1.x targets API v1 (`/v1/` prefix). Breaking API changes get a new prefix with 12-month deprecation window.
- See [full SDK documentation](https://developers.certisigma.ch/sdk) for error codes, webhook semantics, T0 signature format, and compatibility policy.

## Requirements

- Python 3.10+
- `httpx` >= 0.25.0
- `cryptography` >= 44.0.0 (optional, for `certisigma.crypto`)

## License

MIT — Ten Sigma Sagl, Lugano, Switzerland
