Skip to main content

Python SDK for Foritech Secure System.

Project description

Foritech SDK

Локален README за Python пакета. За пълна документация виж ../README.md.

Foritech SDK

Python SDK for Foritech Secure System.

Foritech SDK (Python)

Лек Python SDK за пост-квантова криптография (Kyber768/ML-KEM) и базови операции за ключове и файлово шифриране. Работи самостоятелно (Python) и/или чрез Docker-базирания CLI wrapper.

PyPI пакет: foritech-sdk • Лиценз: MIT • Поддържани Python: 3.11, 3.12, 3.13


Quickstart (60 секунди)

# 1) Инсталиране
pip install -U foritech-sdk

# 2) Минимален пример (encrypt/decrypt)
python - << 'PY'
from foritech import crypto  # пример: модулът ви може да е foritech.crypto

data = b"hello post-quantum world"
aad  = b"example-aad"

ct = crypto.encrypt(data, aad=aad)     # връща {ciphertext, key/nonce/...} или bytes според API
pt = crypto.decrypt(ct, aad=aad)

assert pt == data
print("OK: encrypt/decrypt")
PY

# Foritech CLI Wrapper A — Implementation Pack

This package provides everything you asked for to implement Variant A (Docker-based CLI wrapper), plus the sync note for Variant B. Copy/paste files as indicated.

---

## 0) File tree (new/updated)

repo-root/ ├─ sdk/ │ ├─ pyproject.toml # + console_scripts entry (update) │ └─ src/ │ └─ foritech_cli/ │ ├─ init.py │ └─ main.py # wrapper entrypoint (argparse) ├─ docs/ │ └─ CLI_WRAPPER_SYNC.md # note: how to keep A in sync with B ├─ examples/ │ ├─ 01_keygen.sh # example using wrapper A │ ├─ 02_encrypt_file.sh │ └─ 03_decrypt_file.sh ├─ sdk/tests/ │ └─ test_cli_wrapper_smoke.py # pytest: skipped if no docker ├─ .github/workflows/ │ └─ cli-wrapper-smoke.yml # CI smoke job (Ubuntu) └─ README.md # add short “CLI (Wrapper)” section (update)


> If your SDK code lives under a different subpackage root, adjust imports/paths accordingly.

---

## 1) sdk/src/foritech_cli/__init__.py

```python
__all__ = ["main"]
from .main import main  # re-export for console_scripts hook

2) sdk/src/foritech_cli/main.py (Docker wrapper)

import argparse
import os
import shutil
import subprocess
import sys
from typing import List

# Pin your canonical image here. Replace the digest with your current published image digest.
# You can also read it from an env var (FORITECH_IMAGE) with this as default.
DEFAULT_IMAGE = os.environ.get(
    "FORITECH_IMAGE",
    "ghcr.io/foritech-secure-system/foritech:stable@sha256:YOUR_PINNED_DIGEST_HERE",
)

# Common Docker args. We run as current user where possible to avoid root-owned outputs on Linux.
# On macOS/Windows this just works; on Linux it avoids permission issues.

def _docker_base_args() -> List[str]:
    args = [
        "docker",
        "run",
        "--rm",
        "-i",
    ]

    # Mount current working directory into /work (read-write)
    cwd = os.getcwd()
    args += ["-v", f"{cwd}:/work"]

    # Propagate UID/GID on Linux to avoid root-owned output files
    if os.name == "posix":
        try:
            uid = os.getuid()
            gid = os.getgid()
            args += ["-u", f"{uid}:{gid}"]
        except AttributeError:
            pass

    # Set working dir inside container
    args += ["-w", "/work"]

    # Pass through basic envs if set (helpful for non-interactive CI)
    for env_name in ("TZ", "FORITECH_LOGLEVEL"):
        if env_name in os.environ:
            args += ["-e", f"{env_name}={os.environ[env_name]}"]

    return args


def _ensure_docker_available() -> None:
    if not shutil.which("docker"):
        print("Error: Docker is not installed or not in PATH.", file=sys.stderr)
        sys.exit(127)


def run_container(image: str, inner_args: List[str]) -> int:
    """Run Docker with the given image and inner CLI args. Return the exit code."""
    cmd = _docker_base_args() + [image] + inner_args
    try:
        proc = subprocess.run(cmd, check=False)
        return proc.returncode
    except KeyboardInterrupt:
        return 130
    except Exception as e:
        print(f"Runtime error: {e}", file=sys.stderr)
        return 2


def main(argv: List[str] | None = None) -> int:
    _ensure_docker_available()

    parser = argparse.ArgumentParser(
        prog="foritech",
        description="Foritech CLI (Docker wrapper). This mirrors the real Python CLI (when available).",
    )

    parser.add_argument(
        "--image",
        default=DEFAULT_IMAGE,
        help="Docker image to use (pinned digest recommended). Can also set FORITECH_IMAGE env.",
    )

    sub = parser.add_subparsers(dest="command", required=True)

    # foritech keygen --alg Kyber768 --out key.bin (example)
    p_keygen = sub.add_parser("keygen", help="Generate Kyber768 keypair or KEM material.")
    p_keygen.add_argument("--alg", default="Kyber768", help="Algorithm (default: Kyber768)")
    p_keygen.add_argument("--out", required=True, help="Output path for generated key material")

    # foritech encrypt --in file --out file.enc --aad optional
    p_encrypt = sub.add_parser("encrypt", help="Encrypt file with SDK defaults.")
    p_encrypt.add_argument("--in", dest="in_path", required=True, help="Input file")
    p_encrypt.add_argument("--out", dest="out_path", required=True, help="Output file")
    p_encrypt.add_argument("--aad", dest="aad", default=None, help="Additional authenticated data (optional)")

    # foritech decrypt --in file.enc --out file
    p_decrypt = sub.add_parser("decrypt", help="Decrypt file with SDK defaults.")
    p_decrypt.add_argument("--in", dest="in_path", required=True, help="Input file")
    p_decrypt.add_argument("--out", dest="out_path", required=True, help="Output file")
    p_decrypt.add_argument("--aad", dest="aad", default=None, help="Aad value if used on encrypt (optional)")

    # passthrough: forward unknown commands to the Docker CLI inside (advanced usage)
    p_raw = sub.add_parser("raw", help="Pass through arguments directly to container CLI.")
    p_raw.add_argument("args", nargs=argparse.REMAINDER, help="Arguments passed as-is to container")

    args = parser.parse_args(argv)

    # Map subcommands to inner container CLI invocations.
    # Replace the inner command below with the actual CLI inside your Docker image.
    # Example assumes your container exposes `foritech-cli` with similar verbs.

    inner_cmd = ["foritech-cli"]

    if args.command == "keygen":
        inner = inner_cmd + [
            "keygen",
            "--alg", args.alg,
            "--out", args.out,
        ]
        return run_container(args.image, inner)

    if args.command == "encrypt":
        inner = inner_cmd + [
            "encrypt",
            "--in", args.in_path,
            "--out", args.out_path,
        ]
        if args.aad:
            inner += ["--aad", args.aad]
        return run_container(args.image, inner)

    if args.command == "decrypt":
        inner = inner_cmd + [
            "decrypt",
            "--in", args.in_path,
            "--out", args.out_path,
        ]
        if args.aad:
            inner += ["--aad", args.aad]
        return run_container(args.image, inner)

    if args.command == "raw":
        # Pass everything after 'raw' directly
        return run_container(args.image, inner_cmd + args.args)

    # Should not reach here
    parser.print_usage(sys.stderr)
    return 1


if __name__ == "__main__":
    raise SystemExit(main())

Replace foritech-cli in inner_cmd with the actual command available inside your Docker image. Keep verbs (keygen/encrypt/decrypt) identical to what Variant B will provide.


3) sdk/pyproject.toml (snippet to add console script)

[project.scripts]
foritech = "foritech_cli.main:main"

Place this under the sdk/pyproject.toml [project.scripts] table. If a scripts table already exists, merge the entry.


4) docs/CLI_WRAPPER_SYNC.md (sync note A ↔ B)

# CLI Wrapper A ↔ Python CLI B: Sync Note

**Purpose**: Wrapper A mirrors the future real Python CLI (Variant B). Whenever B changes, update A accordingly.

**Checklist on every B change:**
1. **Subcommands & names** — keep identical verbs (`keygen`, `encrypt`, `decrypt`).
2. **Flags & defaults** — reflect new/changed flags; sync default values.
3. **I/O formats** — keep output format/paths consistent; update examples.
4. **Errors & exit codes** — map exceptions to the same process codes.
5. **Logging levels** — align `--quiet/--verbose` semantics.
6. **Version/help**`--version`/`--help` text and epigraph must match.
7. **Docs/CI** — update README examples and smoke tests as needed.

5) README.md (add short Wrapper section)

## CLI (Wrapper A)

The `foritech` command is a thin Docker-based wrapper that mirrors the upcoming native Python CLI. Requirements: Docker. You can override the image via `FORITECH_IMAGE` env or `--image` flag.

Quickstart:

```bash
foritech --help
foritech keygen --out mykey.bin
foritech encrypt --in secret.txt --out secret.txt.enc
foritech decrypt --in secret.txt.enc --out secret.txt

See docs/CLI_WRAPPER_SYNC.md for how this wrapper stays in sync with the native CLI.


---

## 6) examples (shell)

### examples/01_keygen.sh
```bash
#!/usr/bin/env bash
set -euo pipefail
foritech keygen --out mykey.bin

examples/02_encrypt_file.sh

#!/usr/bin/env bash
set -euo pipefail
: "${INPUT:=secret.txt}"
: "${OUTPUT:=secret.txt.enc}"
foritech encrypt --in "$INPUT" --out "$OUTPUT"

examples/03_decrypt_file.sh

#!/usr/bin/env bash
set -euo pipefail
: "${INPUT:=secret.txt.enc}"
: "${OUTPUT:=secret.txt}"
foritech decrypt --in "$INPUT" --out "$OUTPUT"

Make scripts executable: chmod +x examples/*.sh


7) sdk/tests/test_cli_wrapper_smoke.py

import os
import shutil
import subprocess
import sys
import pytest

pytestmark = pytest.mark.skipif(shutil.which("docker") is None, reason="docker not available")


def test_foritech_help():
    proc = subprocess.run([sys.executable, "-m", "foritech_cli.main", "--help"], capture_output=True, text=True)
    assert proc.returncode == 0
    assert "Foritech CLI" in proc.stdout


def test_foritech_image_override_env(monkeypatch):
    monkeypatch.setenv("FORITECH_IMAGE", "alpine:3.19")
    # We don't actually run a command here to avoid needing the inner CLI
    proc = subprocess.run([sys.executable, "-m", "foritech_cli.main", "--help"], capture_output=True, text=True)
    assert proc.returncode == 0

This is a lightweight smoke test to ensure the module loads and help works. E2E invoking the container is optional here and better suited for a separate job.


8) .github/workflows/cli-wrapper-smoke.yml

name: CLI Wrapper Smoke

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install sdk (editable)
        run: |
          cd sdk
          pip install -U pip
          pip install -e .

      - name: foritech --help should work
        run: |
          foritech --help

This verifies the wrapper installs and prints help on Ubuntu with Docker available in the runner environment.


9) Commit checklist

git add sdk/src/foritech_cli docs/CLI_WRAPPER_SYNC.md examples/ \
        sdk/tests/test_cli_wrapper_smoke.py .github/workflows/cli-wrapper-smoke.yml
# Also update sdk/pyproject.toml and README.md

git add sdk/pyproject.toml README.md

git commit -m "feat(cli): add Docker-based wrapper (Variant A) with smoke tests and docs"

git push -u origin <your-branch>

10) Notes

  • Replace YOUR_PINNED_DIGEST_HERE with the actual image digest you publish.
  • Update inner_cmd to match the command exposed inside your container (foritech-cli is a placeholder).
  • Keep verb names identical to what Variant B will implement to ease transition.

Foritech SDK

Лек Python SDK за криптография и интеграция с Foritech Secure System.

Инсталация

pip install foritech-sdk

##Quickstart

from foritech import __version__
print("foritech-sdk:", __version__)

-(Пример) Шифриране на байтове с Data Encryption Key (DEK)

-Примерният API е минимален и стабилен; ако сменим имена – ще обновим README.

from foritech import errors

def encrypt_bytes(data: bytes, key: bytes) -> bytes:
    # TODO: използвай реалния SDK API при готовност
    if not key:
        raise errors.EncryptError("Missing key")
    return data[::-1]  # demo only

def decrypt_bytes(ct: bytes, key: bytes) -> bytes:
    if not key:
        raise errors.DecryptError("Missing key")
    return ct[::-1]

pt = b"hello"
key = b"\x00" * 32
ct = encrypt_bytes(pt, key)
rt = decrypt_bytes(ct, key)
assert rt == pt

##Полезни връзки

--Репо: https://github.com/foritech-secure-system/foritech-secure-system

--Issues: https://github.com/foritech-secure-system/foritech-secure-system/issues

##Лиценз


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

foritech_sdk-0.1.3.tar.gz (34.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

foritech_sdk-0.1.3-py3-none-any.whl (39.4 kB view details)

Uploaded Python 3

File details

Details for the file foritech_sdk-0.1.3.tar.gz.

File metadata

  • Download URL: foritech_sdk-0.1.3.tar.gz
  • Upload date:
  • Size: 34.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for foritech_sdk-0.1.3.tar.gz
Algorithm Hash digest
SHA256 b1139cdb9bd549c3a086882a824a22e067fc3d502bd52bc82cfa34ac747d3944
MD5 f2f8281803e74a4ed186ed453323c16b
BLAKE2b-256 ed55ec6a283ae44c64c9e174d9000e157fb9e24c949b59cd596ff9e66a2ea3be

See more details on using hashes here.

Provenance

The following attestation bundles were made for foritech_sdk-0.1.3.tar.gz:

Publisher: pypi-release.yml on foritech-secure-system/foritech-secure-system

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file foritech_sdk-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: foritech_sdk-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 39.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for foritech_sdk-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 de658fc0a7fc34fd57c082120f5e00d754ebf13f7b96c4ba893caaa6adea55f4
MD5 da71491b5400ba7493b552c17a501764
BLAKE2b-256 7cfa9f1705c43858c82be02ffec35c9a524e72bfcd9976bb698be6f84f002dd2

See more details on using hashes here.

Provenance

The following attestation bundles were made for foritech_sdk-0.1.3-py3-none-any.whl:

Publisher: pypi-release.yml on foritech-secure-system/foritech-secure-system

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page