Zum Hauptinhalt springen
Venice bietet Privacy-erweiterte Modelle, die in Trusted Execution Environments (TEE) laufen und Ende-zu-Ende-Verschlüsselung (E2EE) unterstützen. Diese Modelle liefern kryptografische Garantien, dass deine Daten privat bleiben — selbst gegenüber Venice.

Die Privacy-Stufen verstehen

TypPräfixWas es bedeutet
TEEtee-*Modell läuft in einer hardware-gesicherten Enklave. Venice kann nicht auf die Berechnung zugreifen. Du kannst das per Attestation verifizieren.
E2EEe2ee-*Vollständige Ende-zu-Ende-Verschlüsselung. Deine Prompts werden clientseitig verschlüsselt, bevor sie gesendet werden. Nur die TEE kann sie entschlüsseln.
E2EE-Modelle umfassen den TEE-Schutz plus clientseitige Verschlüsselung. TEE-Modelle bieten Enklaven-Sicherheit ohne clientseitige Verschlüsselung.

Verfügbare Modelle

Loading…
Die vollständige Liste mit Preisen und Kontext-Limits findest du auf der Modelle-Seite.

TEE-Modelle

TEE-Modelle laufen in hardware-gesicherten Enklaven (Intel TDX, NVIDIA Confidential Computing). Die Modellgewichte und deine Daten sind vor dem Host-System geschützt — einschließlich Venices Infrastruktur.

Grundlegende Nutzung

TEE-Modelle funktionieren genau wie reguläre Modelle:
from openai import OpenAI

client = OpenAI(
    api_key="your-venice-api-key",
    base_url="https://api.venice.ai/api/v1"
)

response = client.chat.completions.create(
    model="tee-qwen3-5-122b-a10b",
    messages=[{"role": "user", "content": "Explain quantum computing"}]
)

print(response.choices[0].message.content)
import OpenAI from 'openai';

const client = new OpenAI({
    apiKey: 'your-venice-api-key',
    baseURL: 'https://api.venice.ai/api/v1'
});

const response = await client.chat.completions.create({
    model: 'tee-qwen3-5-122b-a10b',
    messages: [{ role: 'user', content: 'Explain quantum computing' }]
});

console.log(response.choices[0].message.content);
curl https://api.venice.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY_VENICE" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "tee-qwen3-5-122b-a10b",
    "messages": [{"role": "user", "content": "Explain quantum computing"}]
  }'

TEE-Attestation verifizieren

Du kannst kryptografisch verifizieren, dass ein Modell in einer echten TEE läuft, indem du seinen Attestation-Report abrufst:
# Eine zufällige Nonce erzeugen (verhindert Replay-Angriffe)
NONCE=$(openssl rand -hex 16)

# Attestation abrufen
curl "https://api.venice.ai/api/v1/tee/attestation?model=tee-qwen3-5-122b-a10b&nonce=$NONCE" \
  -H "Authorization: Bearer $API_KEY_VENICE"
import secrets
import requests

nonce = secrets.token_hex(16)

response = requests.get(
    f"https://api.venice.ai/api/v1/tee/attestation",
    params={"model": "tee-qwen3-5-122b-a10b", "nonce": nonce},
    headers={"Authorization": f"Bearer {api_key}"}
)

attestation = response.json()
print(f"Verified: {attestation['verified']}")
print(f"TEE Provider: {attestation['tee_provider']}")
print(f"Model: {attestation['model']}")
Die Attestation-Antwort enthält:
FeldBeschreibung
verifiedOb die Attestation die serverseitige Verifikation bestanden hat
nonceDeine Nonce, bestätigt Frische
modelDie attestierte Modell-ID
tee_providerTEE-Provider-Kennung
intel_quoteRoher Intel-TDX-Quote (base64) für clientseitige Verifikation
nvidia_payloadNVIDIA-GPU-Attestation-Daten (falls zutreffend)
signing_keyÖffentlicher Schlüssel zur Verifikation der Response-Signaturen (typischerweise für E2EE-Flows nötig; bei manchen reinen TEE-Modellen kann er fehlen)
signing_addressAus dem Signing-Key abgeleitete Ethereum-Adresse
In der Produktion die Attestation clientseitig verifizieren, indem du den Intel-TDX-Quote parst und die NVIDIA-Attestation prüfst.
Für die Verifikation reiner TEE-Modelle reichen signing_address und die serverseitigen Verifikationsfelder für Baseline-Attestation-Checks. Ein signing_key wird benötigt, wenn du clientseitige E2EE-Schlüsselverhandlung und strikte Key-Binding-Checks brauchst.

Response-Signaturen

TEE-Modelle können ihre Antworten signieren und so beweisen, dass die Ausgabe aus der attestierten Enklave stammt:
# Nach einer Completion die Signatur verifizieren
curl "https://api.venice.ai/api/v1/tee/signature?model=tee-qwen3-5-122b-a10b&request_id=chatcmpl-abc123" \
  -H "Authorization: Bearer $API_KEY_VENICE"
response = requests.get(
    f"https://api.venice.ai/api/v1/tee/signature",
    params={"model": "tee-qwen3-5-122b-a10b", "request_id": completion_id},
    headers={"Authorization": f"Bearer {api_key}"}
)

signature = response.json()
# Prüfen, ob Signatur zur signing_address aus der Attestation passt

E2EE-Modelle

E2EE-Modelle fügen dem TEE-Schutz clientseitige Verschlüsselung hinzu. Deine Prompts werden verschlüsselt, bevor sie dein Gerät verlassen, und nur die TEE kann sie entschlüsseln. Venice E2EE verwendet:
  • ECDH (Elliptic Curve Diffie-Hellman) auf secp256k1 für Schlüsselaustausch
  • HKDF-SHA256 für Schlüsselableitung
  • AES-256-GCM für symmetrische Verschlüsselung
  • TEE-Attestation, um zu verifizieren, dass das Modell in einer sicheren Enklave läuft
E2EE erfordert eine clientseitige Implementierung. Die Beispiele unten zeigen das vollständige Protokoll.

Wie E2EE funktioniert

1

Ephemeren Schlüsselpaar generieren

Der Client erzeugt ein secp256k1-Schlüsselpaar für diese Session.
2

TEE-Attestation abrufen

Der Client fragt /api/v1/tee/attestation an und erhält den Public Key des Modells, Attestation-Evidence und eine Nonce.
3

Attestation verifizieren

Der Client prüft Nonce-Übereinstimmung, deaktivierten Debug-Modus und Attestation-Gültigkeit.
4

Nachrichten verschlüsseln

Der Client verschlüsselt die Prompts mittels ECDH-Shared-Secret → HKDF → AES-GCM.
5

Request senden

Der Client sendet den Request mit E2EE-Headern (X-Venice-TEE-Client-Pub-Key, X-Venice-TEE-Model-Pub-Key, X-Venice-TEE-Signing-Algo).
6

TEE-Processing

Die TEE entschlüsselt den Request, verarbeitet ihn und verschlüsselt die Antwort.
7

Response entschlüsseln

Der Client erhält verschlüsselte Chunks und entschlüsselt sie mit dem Private Key.

Voraussetzungen

JavaScript (Node.js ESM):
npm install elliptic @noble/ciphers @noble/hashes
Python:
pip install cryptography ecdsa requests

Schritt 1: E2EE-Support des Modells prüfen

Zuerst über den /models-Endpoint prüfen, ob das Modell E2EE unterstützt.
async function getE2EEModels(apiKey) {
  const response = await fetch('https://api.venice.ai/api/v1/models', {
    headers: { Authorization: `Bearer ${apiKey}` },
  })
  const { data } = await response.json()

  return data.filter(model => model.model_spec?.capabilities?.supportsE2EE === true)
}

// Beispielnutzung
const models = await getE2EEModels('your-api-key')
console.log('E2EE Models:', models.map(m => m.id))
// Output: ['e2ee-qwen3-5-122b-a10b', 'e2ee-glm-5', ...]
import requests

def get_e2ee_models(api_key: str) -> list:
    """Get list of models that support E2EE."""
    response = requests.get(
        'https://api.venice.ai/api/v1/models',
        headers={'Authorization': f'Bearer {api_key}'}
    )
    models = response.json()['data']

    return [
        model for model in models
        if model.get('model_spec', {}).get('capabilities', {}).get('supportsE2EE')
    ]

# Beispielnutzung
models = get_e2ee_models('your-api-key')
print('E2EE Models:', [m['id'] for m in models])

Schritt 2: Ephemeres Schlüsselpaar generieren

Pro Session ein neues Schlüsselpaar generieren. Der Private Key sollte nur im Memory gehalten und nach Gebrauch sicher genullt werden.
import { ec as EC } from 'elliptic'

function generateEphemeralKeyPair() {
  const ec = new EC('secp256k1')
  const keyPair = ec.genKeyPair()

  return {
    privateKey: new Uint8Array(keyPair.getPrivate().toArray('be', 32)),
    publicKeyHex: keyPair.getPublic('hex'), // Unkomprimiertes Format (65 Bytes hex)
  }
}

// Sicherheit: Private Key nach Gebrauch nullen
function zeroFill(arr) {
  arr.fill(0)
}
from ecdsa import SECP256k1, SigningKey
import secrets

def generate_ephemeral_key_pair():
    """Generate ephemeral secp256k1 key pair for E2EE session."""
    private_key = SigningKey.generate(curve=SECP256k1)
    public_key = private_key.get_verifying_key()

    # Unkomprimiertes Public Key holen (04 || x || y)
    public_key_bytes = b'\x04' + public_key.to_string()

    return {
        'private_key': private_key.to_string(),  # 32 Bytes
        'public_key_hex': public_key_bytes.hex()  # 130 Hex-Zeichen
    }

Validierungs-Helpers

Verwende diese Helper-Funktionen, um Schlüssel und verschlüsselten Content vor dem Senden zu validieren.
function validateClientPubkey(pubkeyHex) {
  if (pubkeyHex.length !== 130 || !pubkeyHex.startsWith('04')) {
    throw new Error(`Client pubkey must be 130 hex chars starting with '04' (got ${pubkeyHex.length})`)
  }
}

function isValidEncrypted(s) {
  // Minimum: ephemeral_pub (65) + nonce (12) + tag (16) = 93 Bytes = 186 Hex-Zeichen
  return s.length >= 186 && /^[0-9a-fA-F]+$/.test(s)
}
def validate_client_pubkey(pubkey_hex: str) -> None:
    """Validate client public key format."""
    if len(pubkey_hex) != 130 or not pubkey_hex.startswith('04'):
        raise ValueError(f"Client pubkey must be 130 hex chars starting with '04' (got {len(pubkey_hex)})")

def is_valid_encrypted(s: str) -> bool:
    """Check if string is valid hex-encrypted content."""
    # Minimum: ephemeral_pub (65) + nonce (12) + tag (16) = 93 Bytes = 186 Hex-Zeichen
    return len(s) >= 186 and all(c in '0123456789abcdefABCDEF' for c in s)

Schritt 3: TEE-Attestation abrufen und verifizieren

Die Attestation beweist, dass das Modell in einer echten TEE läuft. Immer die Attestation verifizieren, bevor du dem Public Key des Modells vertraust.
Wichtig: Nonce-Länge — Die Client-Nonce muss 32 Bytes (64 Hex-Zeichen) lang sein. Manche TEE-Provider verlangen exakt 32 Bytes und lehnen kürzere Nonces ab.
import crypto from 'crypto'

async function fetchAndVerifyAttestation(modelId, apiKey) {
  // Client-Nonce für Replay-Schutz generieren (32 Bytes = 64 Hex-Zeichen)
  const clientNonce = crypto.randomBytes(32).toString('hex')

  const response = await fetch(
    `https://api.venice.ai/api/v1/tee/attestation?model=${encodeURIComponent(modelId)}&nonce=${clientNonce}`,
    { headers: { Authorization: `Bearer ${apiKey}` } }
  )

  const attestation = await response.json()

  // Attestation verifizieren
  if (attestation.verified !== true) {
    throw new Error('TEE attestation verification failed on server')
  }

  if (attestation.nonce !== clientNonce) {
    throw new Error('Attestation nonce mismatch - possible replay attack')
  }

  // Public Key des Modells für Verschlüsselung holen
  const modelPublicKey = attestation.signing_key || attestation.signing_public_key
  if (!modelPublicKey) {
    throw new Error('No signing key in attestation response')
  }

  return {
    modelPublicKey,
    signingAddress: attestation.signing_address,
    attestation,
  }
}
import secrets
import requests

def fetch_and_verify_attestation(model_id: str, api_key: str) -> dict:
    """Fetch and verify TEE attestation for a model."""
    # Client-Nonce für Replay-Schutz generieren (32 Bytes = 64 Hex-Zeichen)
    client_nonce = secrets.token_hex(32)

    response = requests.get(
        f'https://api.venice.ai/api/v1/tee/attestation',
        params={'model': model_id, 'nonce': client_nonce},
        headers={'Authorization': f'Bearer {api_key}'}
    )
    attestation = response.json()

    # Attestation verifizieren
    if attestation.get('verified') != True:
        raise ValueError('TEE attestation verification failed on server')

    if attestation.get('nonce') != client_nonce:
        raise ValueError('Attestation nonce mismatch - possible replay attack')

    # Public Key des Modells für Verschlüsselung holen
    model_public_key = attestation.get('signing_key') or attestation.get('signing_public_key')
    if not model_public_key:
        raise ValueError('No signing key in attestation response')

    return {
        'model_public_key': model_public_key,
        'signing_address': attestation.get('signing_address'),
        'attestation': attestation
    }

Schritt 4: Nachrichten verschlüsseln

User- und System-Nachrichten vor dem Senden verschlüsseln. Nur Nachrichten mit Rolle user und system müssen verschlüsselt werden.
Wenn E2EE-Header gesetzt sind, müssen alle Nachrichten mit Rolle user und system verschlüsselt sein. Sendet du in diesen Rollen Klartext, gibt es einen „Encrypted field is not valid hex”-Fehler.
import { gcm } from '@noble/ciphers/aes.js'
import { hkdf } from '@noble/hashes/hkdf.js'
import { sha256 } from '@noble/hashes/sha2.js'
import { ec as EC } from 'elliptic'
import crypto from 'crypto'

const HKDF_INFO = new TextEncoder().encode('ecdsa_encryption')

function encryptMessage(plaintext, modelPublicKeyHex) {
  const ec = new EC('secp256k1')

  // Public Key normalisieren (04-Präfix ggf. ergänzen)
  let normalizedKey = modelPublicKeyHex
  if (!normalizedKey.startsWith('04') && normalizedKey.length === 128) {
    normalizedKey = '04' + normalizedKey
  }

  const modelPublicKey = ec.keyFromPublic(normalizedKey, 'hex')

  // Ephemeres Schlüsselpaar für diese Nachricht generieren
  const ephemeralKeyPair = ec.genKeyPair()

  // ECDH-Shared-Secret
  const sharedSecret = ephemeralKeyPair.derive(modelPublicKey.getPublic())
  const sharedSecretBytes = new Uint8Array(sharedSecret.toArray('be', 32))

  // AES-Key per HKDF ableiten
  const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

  // Zufällige Nonce
  const nonce = crypto.randomBytes(12)

  // Mit AES-GCM verschlüsseln
  const cipher = gcm(aesKey, nonce)
  const encrypted = cipher.encrypt(new TextEncoder().encode(plaintext))

  // Ephemeren Public Key holen (unkomprimiert)
  const ephemeralPublic = new Uint8Array(ephemeralKeyPair.getPublic(false, 'array'))

  // Kombinieren: ephemeral_public (65 Bytes) + nonce (12 Bytes) + ciphertext
  const result = new Uint8Array(65 + 12 + encrypted.length)
  result.set(ephemeralPublic, 0)
  result.set(nonce, 65)
  result.set(encrypted, 65 + 12)

  return Buffer.from(result).toString('hex')
}

function encryptMessagesForE2EE(messages, modelPublicKey) {
  return messages.map(msg => {
    if (msg.role === 'user' || msg.role === 'system') {
      return {
        ...msg,
        content: encryptMessage(msg.content, modelPublicKey),
      }
    }
    return msg
  })
}
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from ecdsa import SECP256k1, VerifyingKey, SigningKey
import os

HKDF_INFO = b'ecdsa_encryption'

def encrypt_message(plaintext: str, model_public_key_hex: str) -> str:
    """Encrypt a message using ECDH + HKDF + AES-GCM."""
    # Public Key normalisieren
    key_hex = model_public_key_hex
    if not key_hex.startswith('04') and len(key_hex) == 128:
        key_hex = '04' + key_hex

    model_public_key_bytes = bytes.fromhex(key_hex)

    # Public Key des Modells parsen (04-Präfix überspringen)
    model_verifying_key = VerifyingKey.from_string(
        model_public_key_bytes[1:],
        curve=SECP256k1
    )

    # Ephemeres Schlüsselpaar für diese Nachricht generieren
    ephemeral_private = SigningKey.generate(curve=SECP256k1)
    ephemeral_public = ephemeral_private.get_verifying_key()

    # ECDH: Shared-Secret berechnen
    shared_point = model_verifying_key.pubkey.point * ephemeral_private.privkey.secret_multiplier
    shared_secret = shared_point.x().to_bytes(32, 'big')

    # AES-Key per HKDF ableiten
    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=None,
        info=HKDF_INFO,
    )
    aes_key = hkdf.derive(shared_secret)

    # Zufällige Nonce
    nonce = os.urandom(12)

    # Mit AES-GCM verschlüsseln
    aesgcm = AESGCM(aes_key)
    ciphertext = aesgcm.encrypt(nonce, plaintext.encode('utf-8'), None)

    # Ephemeren Public Key holen (unkomprimiert: 04 || x || y)
    ephemeral_public_bytes = b'\x04' + ephemeral_public.to_string()

    # Kombinieren: ephemeral_public (65 Bytes) + nonce (12 Bytes) + ciphertext
    result = ephemeral_public_bytes + nonce + ciphertext

    return result.hex()

def encrypt_messages_for_e2ee(messages: list, model_public_key: str) -> list:
    """Encrypt user and system messages."""
    encrypted_messages = []
    for msg in messages:
        if msg['role'] in ('user', 'system'):
            encrypted_messages.append({
                **msg,
                'content': encrypt_message(msg['content'], model_public_key)
            })
        else:
            encrypted_messages.append(msg)
    return encrypted_messages

Schritt 5: Request mit E2EE-Headern senden

Erforderliche Header setzen, um E2EE-Processing zu aktivieren.
HeaderBeschreibung
X-Venice-TEE-Client-Pub-KeyDein ephemerer Public Key (unkomprimiertes Hex, 130 Zeichen)
X-Venice-TEE-Model-Pub-KeyPublic Key des Modells aus der Attestation
X-Venice-TEE-Signing-AlgoImmer ecdsa
async function sendE2EERequest(messages, model, e2eeContext, apiKey) {
  // Nachrichten verschlüsseln
  const encryptedMessages = encryptMessagesForE2EE(messages, e2eeContext.modelPublicKey)

  const response = await fetch('https://api.venice.ai/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
      // E2EE-Header
      'X-Venice-TEE-Client-Pub-Key': e2eeContext.publicKeyHex,
      'X-Venice-TEE-Model-Pub-Key': e2eeContext.modelPublicKey,
      'X-Venice-TEE-Signing-Algo': 'ecdsa',
    },
    body: JSON.stringify({
      model,
      messages: encryptedMessages,
      stream: true, // E2EE benötigt Streaming
    }),
  })

  return response
}
import requests

def send_e2ee_request(
    messages: list,
    model: str,
    e2ee_context: dict,
    api_key: str
) -> requests.Response:
    """Send an E2EE-encrypted chat completion request."""
    # Nachrichten verschlüsseln
    encrypted_messages = encrypt_messages_for_e2ee(
        messages,
        e2ee_context['model_public_key']
    )

    response = requests.post(
        'https://api.venice.ai/api/v1/chat/completions',
        headers={
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json',
            # E2EE-Header
            'X-Venice-TEE-Client-Pub-Key': e2ee_context['public_key_hex'],
            'X-Venice-TEE-Model-Pub-Key': e2ee_context['model_public_key'],
            'X-Venice-TEE-Signing-Algo': 'ecdsa'
        },
        json={
            'model': model,
            'messages': encrypted_messages,
            'stream': True  # E2EE benötigt Streaming
        },
        stream=True
    )

    return response

Schritt 6: Response-Chunks entschlüsseln

Antworten von E2EE-Modellen sind hex-kodierte verschlüsselte Chunks. Entschlüssele jeden Chunk mit deinem Private Key.
import { gcm } from '@noble/ciphers/aes.js'
import { hkdf } from '@noble/hashes/hkdf.js'
import { sha256 } from '@noble/hashes/sha2.js'
import { ec as EC } from 'elliptic'

const HKDF_INFO = new TextEncoder().encode('ecdsa_encryption')

function hexToBytes(hex) {
  const h = hex.startsWith('0x') ? hex.slice(2) : hex
  const bytes = new Uint8Array(h.length / 2)
  for (let i = 0; i < bytes.length; i++) {
    bytes[i] = parseInt(h.substring(i * 2, i * 2 + 2), 16)
  }
  return bytes
}

function isHexEncrypted(s) {
  // Minimum: ephemeral_pub (65) + nonce (12) + tag (16) = 93 Bytes = 186 Hex-Zeichen
  if (s.length < 186) return false
  return /^[0-9a-fA-F]+$/.test(s)
}

function decryptChunk(ciphertextHex, clientPrivateKey) {
  const raw = hexToBytes(ciphertextHex)

  // Komponenten parsen
  const serverEphemeralPubKey = raw.slice(0, 65)
  const nonce = raw.slice(65, 65 + 12)
  const ciphertext = raw.slice(65 + 12)

  // ECDH mit dem ephemeren Schlüssel des Servers
  const ec = new EC('secp256k1')
  const clientKey = ec.keyFromPrivate(Buffer.from(clientPrivateKey))
  const serverKey = ec.keyFromPublic(Buffer.from(serverEphemeralPubKey))
  const sharedSecret = clientKey.derive(serverKey.getPublic())
  const sharedSecretBytes = new Uint8Array(sharedSecret.toArray('be', 32))

  // AES-Key ableiten
  const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

  // Entschlüsseln
  const cipher = gcm(aesKey, nonce)
  const plaintext = cipher.decrypt(ciphertext)

  return new TextDecoder().decode(plaintext)
}

// Streaming-Response verarbeiten
async function processE2EEStream(response, clientPrivateKey) {
  const reader = response.body.getReader()
  const decoder = new TextDecoder()
  let fullContent = ''

  while (true) {
    const { done, value } = await reader.read()
    if (done) break

    const text = decoder.decode(value)
    const lines = text.split('\n')

    for (const line of lines) {
      if (!line.startsWith('data: ')) continue
      const data = line.slice(6)
      if (data === '[DONE]') continue

      try {
        const chunk = JSON.parse(data)
        const content = chunk.choices?.[0]?.delta?.content

        if (content && isHexEncrypted(content)) {
          const decrypted = decryptChunk(content, clientPrivateKey)
          fullContent += decrypted
          process.stdout.write(decrypted) // Echtzeit-Ausgabe
        } else if (content) {
          fullContent += content
          process.stdout.write(content)
        }
      } catch (e) {
        // Fehlerhafte Chunks überspringen
      }
    }
  }

  return fullContent
}
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from ecdsa import SECP256k1, VerifyingKey, SigningKey
import json
import re

HKDF_INFO = b'ecdsa_encryption'

def is_hex_encrypted(s: str) -> bool:
    """Check if string looks like hex-encrypted content."""
    if len(s) < 186:  # Minimum: 65 + 12 + 16 = 93 Bytes = 186 Hex
        return False
    return bool(re.match(r'^[0-9a-fA-F]+$', s))

def decrypt_chunk(ciphertext_hex: str, client_private_key: bytes) -> str:
    """Decrypt an E2EE response chunk."""
    raw = bytes.fromhex(ciphertext_hex)

    # Komponenten parsen
    server_ephemeral_pub = raw[:65]
    nonce = raw[65:77]
    ciphertext = raw[77:]

    # Ephemeren Public Key des Servers parsen (04-Präfix überspringen)
    server_verifying_key = VerifyingKey.from_string(
        server_ephemeral_pub[1:],
        curve=SECP256k1
    )

    # Private Key des Clients rekonstruieren
    client_signing_key = SigningKey.from_string(client_private_key, curve=SECP256k1)

    # ECDH: Shared-Secret berechnen
    shared_point = server_verifying_key.pubkey.point * client_signing_key.privkey.secret_multiplier
    shared_secret = shared_point.x().to_bytes(32, 'big')

    # AES-Key ableiten
    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=None,
        info=HKDF_INFO,
    )
    aes_key = hkdf.derive(shared_secret)

    # Entschlüsseln
    aesgcm = AESGCM(aes_key)
    plaintext = aesgcm.decrypt(nonce, ciphertext, None)

    return plaintext.decode('utf-8')

def process_e2ee_stream(response, client_private_key: bytes) -> str:
    """Process streaming E2EE response."""
    full_content = ''

    for line in response.iter_lines():
        if not line:
            continue

        line_str = line.decode('utf-8')
        if not line_str.startswith('data: '):
            continue

        data = line_str[6:]
        if data == '[DONE]':
            continue

        try:
            chunk = json.loads(data)
            content = chunk.get('choices', [{}])[0].get('delta', {}).get('content', '')

            if content and is_hex_encrypted(content):
                decrypted = decrypt_chunk(content, client_private_key)
                full_content += decrypted
                print(decrypted, end='', flush=True)  # Echtzeit-Ausgabe
            elif content:
                full_content += content
                print(content, end='', flush=True)
        except json.JSONDecodeError:
            pass

    print()  # Abschluss-Newline
    return full_content

Vollständiges funktionierendes Beispiel

import elliptic from 'elliptic';
import { gcm } from '@noble/ciphers/aes.js';
import { hkdf } from '@noble/hashes/hkdf.js';
import { sha256 } from '@noble/hashes/sha2.js';
import crypto from 'crypto';

const EC = elliptic.ec;

const API_KEY = process.env.API_KEY_VENICE;
const BASE_URL = 'https://api.venice.ai/api/v1';
const MODEL = 'e2ee-qwen3-5-122b-a10b';
const HKDF_INFO = new TextEncoder().encode('ecdsa_encryption');

function hexToBytes(hex) {
  const bytes = new Uint8Array(hex.length / 2);
  for (let i = 0; i < bytes.length; i++) {
    bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
  }
  return bytes;
}

async function main() {
  // Schritt 1: Ephemeres Schlüsselpaar generieren
  console.log('🔑 Generating ephemeral key pair...');
  const ec = new EC('secp256k1');
  const keyPair = ec.genKeyPair();
  const clientPublicKeyHex = keyPair.getPublic('hex');

  // Schritt 2: Attestation abrufen und verifizieren
  console.log('🔍 Fetching TEE attestation...');
  const clientNonce = crypto.randomBytes(32).toString('hex'); // 32 Bytes erforderlich
  const attestationRes = await fetch(
    `${BASE_URL}/tee/attestation?model=${MODEL}&nonce=${clientNonce}`,
    { headers: { Authorization: `Bearer ${API_KEY}` } }
  );
  const attestation = await attestationRes.json();

  if (attestation.verified !== true || attestation.nonce !== clientNonce) {
    throw new Error('Attestation verification failed');
  }

  const modelPublicKey = attestation.signing_key || attestation.signing_public_key;
  console.log('✅ TEE attestation verified');

  // Schritt 3: Nachricht verschlüsseln
  console.log('🔐 Encrypting message...');
  const plaintext = 'What is 2+2? Answer briefly.';

  // Public Key des Modells normalisieren und parsen
  let normalizedKey = modelPublicKey;
  if (!normalizedKey.startsWith('04') && normalizedKey.length === 128) {
    normalizedKey = '04' + normalizedKey;
  }

  const modelKey = ec.keyFromPublic(normalizedKey, 'hex');
  const ephemeralKeyPair = ec.genKeyPair();
  const sharedSecret = ephemeralKeyPair.derive(modelKey.getPublic());
  const sharedSecretBytes = new Uint8Array(sharedSecret.toArray('be', 32));
  const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32);
  const nonce = crypto.randomBytes(12);
  const cipher = gcm(aesKey, nonce);
  const encrypted = cipher.encrypt(new TextEncoder().encode(plaintext));
  const ephemeralPublic = new Uint8Array(ephemeralKeyPair.getPublic(false, 'array'));

  const result = new Uint8Array(65 + 12 + encrypted.length);
  result.set(ephemeralPublic, 0);
  result.set(nonce, 65);
  result.set(encrypted, 77);

  const encryptedContent = Buffer.from(result).toString('hex');
  const messages = [{ role: 'user', content: encryptedContent }];

  // Schritt 4: E2EE-Request senden
  console.log('📤 Sending encrypted request...');
  const response = await fetch(`${BASE_URL}/chat/completions`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
      'X-Venice-TEE-Client-Pub-Key': clientPublicKeyHex,
      'X-Venice-TEE-Model-Pub-Key': modelPublicKey,
      'X-Venice-TEE-Signing-Algo': 'ecdsa',
    },
    body: JSON.stringify({ model: MODEL, messages, stream: true }),
  });

  // Schritt 5: Antwort entschlüsseln
  console.log('📥 Decrypting response...\n');
  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const text = decoder.decode(value);
    for (const line of text.split('\n')) {
      if (!line.startsWith('data: ') || line.includes('[DONE]')) continue;

      try {
        const chunk = JSON.parse(line.slice(6));
        const content = chunk.choices?.[0]?.delta?.content;
        if (!content) continue;

        if (/^[0-9a-fA-F]+$/.test(content) && content.length >= 186) {
          // Entschlüsseln
          const raw = hexToBytes(content);
          const serverEphemeralPub = raw.slice(0, 65);
          const nonce = raw.slice(65, 77);
          const ciphertext = raw.slice(77);

          const serverKey = ec.keyFromPublic(Buffer.from(serverEphemeralPub));
          const sharedSecret = keyPair.derive(serverKey.getPublic());
          const aesKey = hkdf(sha256, new Uint8Array(sharedSecret.toArray('be', 32)), undefined, HKDF_INFO, 32);
          const cipher = gcm(aesKey, nonce);
          const plaintext = new TextDecoder().decode(cipher.decrypt(ciphertext));
          process.stdout.write(plaintext);
        } else {
          process.stdout.write(content);
        }
      } catch {}
    }
  }

  console.log('\n\n🔐 Response decrypted end-to-end');
}

main().catch(console.error);

E2EE-Einschränkungen

E2EE hat einige Einschränkungen aufgrund der Verschlüsselungsanforderungen:
FeatureStatus
StreamingPflicht (Non-Streaming nicht unterstützt)
Web SearchDeaktiviert (würde Inhalte leaken)
Datei-UploadsNicht unterstützt
Function CallingNicht unterstützt
Venice-System-PromptDeaktiviert (muss clientseitig verschlüsselt werden)

Sicherheits-Best-Practices

  1. Pro Session neues Schlüsselpaar — keine ephemeren Keys wiederverwenden
  2. Private Keys nullen — Private-Key-Bytes nach Gebrauch aus dem Memory löschen
  3. Attestation verifizieren — immer verified: true und Nonce-Match prüfen
  4. Debug-Modus prüfen — Attestations von Debug-Enklaven ablehnen
  5. Streaming nutzen — E2EE benötigt Streaming für korrektes Chunking
  6. Fehler sauber behandeln — Decryption-Fehler nicht an Nutzer durchreichen
  7. 32-Byte-Nonces nutzen — TEE-Provider verlangen genau 32 Bytes

Best Practices

Verlasse dich nicht nur auf das verified: true. Parse den Intel-TDX-Quote clientseitig und prüfe, dass die Messungen den erwarteten Werten entsprechen. Für NVIDIA-GPUs die Attestation über NVIDIAs Verifikationsdienst prüfen.
Für jede Attestation-Anfrage eine neue zufällige Nonce erzeugen. Das verhindert Replay-Angriffe, bei denen ein Angreifer eine veraltete Attestation ausliefern könnte.
Der Signing-Key sollte an das TDX-REPORTDATA-Feld gebunden sein. Das beweist, dass der Schlüssel innerhalb der Enklave erzeugt wurde.
Sicherstellen, dass die TDX-Attestation keine Debug-Flags gesetzt hat. Eine Debug-Enklave kann inspiziert werden und sollte in der Produktion nicht vertraut werden.
E2EE erfordert sorgfältige kryptografische Implementierung. Nutze unsere offiziellen SDKs, statt das Protokoll selbst zu implementieren.

Modell-Fähigkeiten prüfen

Du kannst über den Models-Endpoint prüfen, ob ein Modell TEE oder E2EE unterstützt:
curl https://api.venice.ai/api/v1/models \
  -H "Authorization: Bearer $API_KEY_VENICE" | jq '.data[] | select(.model_spec.capabilities.supportsTeeAttestation == true or .model_spec.capabilities.supportsE2EE == true) | {id, tee: .model_spec.capabilities.supportsTeeAttestation, e2ee: .model_spec.capabilities.supportsE2EE}'
models = client.models.list()

for model in models.data:
    caps = getattr(model, 'model_spec', {}).get('capabilities', {})
    if caps.get('supportsTeeAttestation') or caps.get('supportsE2EE'):
        print(f"{model.id}: TEE={caps.get('supportsTeeAttestation')}, E2EE={caps.get('supportsE2EE')}")

Fehlerbehandlung

FehlerUrsacheLösung
TEE attestation verification failedAttestation hat die Validierung nicht bestandenErneut versuchen oder Support kontaktieren
Attestation nonce mismatchMöglicher Replay-AngriffFrische Nonce generieren
TDX debug mode detectedEnklave ist im Debug-ModusNicht für Produktion verwenden
Failed to decrypt fieldE2EE-Decryption serverseitig fehlgeschlagenEigene Verschlüsselungs-Implementierung prüfen
E2EE requires streamingNon-Streaming-Request an E2EE-Modellstream: true setzen
Encrypted field is not valid hexKlartext mit E2EE-Headern gesendetAlle User-/System-Nachrichten verschlüsseln
Invalid public keyFalsches Key-Format130 Hex-Zeichen beginnend mit 04 verwenden

Fehlerbehebung

Die Nonce-Länge ist falsch. TEE-Provider verlangen exakt 32 Bytes (64 Hex-Zeichen).
  • crypto.randomBytes(32).toString('hex') (JS) oder secrets.token_hex(32) (Python) verwenden
  • Häufiger Fehler: secrets.token_hex(16) erzeugt 32 Hex-Zeichen (16 Bytes), nicht 32 Bytes
  • Prüfen, ob das Modell E2EE unterstützt (supportsE2EE: true)
  • API-Schlüssel-Gültigkeit und Zugriff auf das angeforderte Modell verifizieren
  • Netzwerkverbindung zur Venice-API prüfen
  • Sicherstellen, dass derselbe Private Key verwendet wird, der den in den Headern gesendeten Public Key erzeugt hat
  • Prüfen, dass der Response-Content tatsächlich hex-kodiert ist (E2EE aktiv)
  • Verifizieren, dass der Model-Public-Key dem für die Verschlüsselung verwendeten entspricht
  • Alle user- und system-Nachrichten müssen verschlüsselt sein, wenn E2EE-Header vorhanden sind
  • Sicherstellen, dass der verschlüsselte Content die isValidEncrypted()-Validierung passiert (mindestens 186 Hex-Zeichen)
  • Prüfen, dass die Verschlüsselungs-Ausgabe lowercase-Hex ohne Präfixe ist
  • Client-Public-Key muss exakt 130 Hex-Zeichen sein und mit 04 beginnen
  • Das Helper-validateClientPubkey() vor dem Senden zur Formatprüfung verwenden
  • Sicherstellen, dass unkomprimiertes Public-Key-Format verwendet wird (65 Bytes = 130 Hex-Zeichen)
  • Modell-ID prüfen und sicherstellen, dass das Modell E2EE unterstützt
  • /models-Endpoint nutzen, um verfügbare E2EE-Modelle zu verifizieren

Ressourcen