Metadata-Version: 2.4
Name: sevola-encrypt-sdk
Version: 1.0.0
Summary: Python SDK for Sevola Encrypt.
Author-email: Ifabula <anggit@ifabula.com>
License: MIT
Project-URL: Homepage, https://gitlab.com/ifabula-digital-kreasi/sevola-encrypt-sdk-python
Project-URL: Issues, https://gitlab.com/ifabula-digital-kreasi/sevola-encrypt-sdk-python/issues
Keywords: encryption,fpe,ff1,aes,sevola,ubiq
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.8
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.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=41.0
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: respx>=0.20; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: mypy>=1.5; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Dynamic: license-file

# Sevola Encrypt SDK (Python)

Python SDK for Sevola Encrypt API. Ciphertext-interoperable with the .NET `SevolaEncryptSdk` package.

## Install

```bash
pip install sevola-encrypt-sdk
```

Requires Python 3.8+.

## Quick start (sync)

```python
from sevola_encrypt_sdk import EncryptClient, EncryptPayload, DecryptPayload

with EncryptClient(api_key="YOUR_KEY", api_url="https://api.sevola.example") as client:
    # Transit (AES-CBC)
    cipher = client.encrypt_transit("hello world")
    plain  = client.decrypt_transit(cipher)

    # Transit dynamic (per-request key, with x-sevola-traceid suffix)
    cipher = client.encrypt_transit_dynamic("hello world")
    plain  = client.decrypt_transit_dynamic(cipher)

    # FF1 bulk (format-preserving encryption)
    enc = client.encrypt_data([
        EncryptPayload(template="numeric",      data="081234567890"),
        EncryptPayload(template="alphanumeric", data="JohnDoe"),
    ])
    dec = client.decrypt_data([
        DecryptPayload(template="numeric",      value=enc[0].value),
        DecryptPayload(template="alphanumeric", value=enc[1].value),
    ])
```

## Quick start (async)

```python
import asyncio
from sevola_encrypt_sdk import AsyncEncryptClient

async def main():
    async with AsyncEncryptClient(api_key="YOUR_KEY", api_url="https://api.sevola.example") as client:
        cipher = await client.encrypt_transit("hello async")
        plain  = await client.decrypt_transit(cipher)
        print(plain)

asyncio.run(main())
```

## Public API

| Method                            | Returns                | Description |
|-----------------------------------|------------------------|-------------|
| `encrypt_transit(plain)`          | `str`                  | AES-CBC + PBKDF2 transit cipher (Base64). |
| `decrypt_transit(cipher)`         | `str`                  | Reverse of `encrypt_transit`. |
| `encrypt_transit_dynamic(plain)`  | `str`                  | Same as transit, key fetched per request. Output ends with `x-sevola-traceid=...`. |
| `decrypt_transit_dynamic(cipher)` | `str`                  | Decrypts dynamic transit cipher. |
| `encrypt_data(payloads)`          | `list[EncryptResult]`  | FF1 bulk encryption (templates: `numeric`, `alphanumeric`). |
| `decrypt_data(payloads)`          | `list[DecryptResult]`  | FF1 bulk decryption. |
| `clear_cache()`                   | `None`                 | Clears the in-memory key cache. |

## Interoperability with .NET

This SDK reproduces the exact crypto pipeline of the .NET `SevolaEncryptSdk`:

- **Transit:** AES-256-CBC with PKCS7. Key derived via PBKDF2-HMAC-SHA256, 1000 iterations, salt `FixedSalt123456`, 32-byte output. IV (16 bytes) prepended to ciphertext, output Base64-encoded.
- **Response decryption:** AES-256-GCM. Key = SHA-256(api_key). 12-byte nonce prepended; 16-byte tag appended; output Base64-encoded by server.
- **FF1:** NIST SP 800-38G — algorithm vendored from [`ubiq-security-fpe`](https://gitlab.com/ubiqsecurity/ubiq-fpe-python) (MIT licensed) with M2Crypto replaced by `cryptography` so installs are dependency-free. The cryptographic logic is **byte-identical** to upstream and to `UbiqSecurity.Fpe` in .NET. Tweak `U8dXq2a+1kA=` (Base64), radix 10 / alphabet `0-9` for `numeric`, radix 62 / alphabet `0-9a-zA-Z` for `alphanumeric`.
- **Markers:** Same `Z9Y` / `9078` family of base, padding, and escape markers.

## License

MIT
