> ## Documentation Index
> Fetch the complete documentation index at: https://veniceai-mintlify-6ce01df5.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# TEE 및 E2EE 모델

> Trusted Execution Environment와 End-to-End Encryption을 활용한 프라이버시 강화 AI

Venice는 Trusted Execution Environment(TEE)에서 실행되고 End-to-End Encryption(E2EE)을 지원하는 프라이버시 강화 모델을 제공합니다. 이 모델들은 사용자의 데이터가—Venice조차도 접근할 수 없도록—프라이빗하게 유지된다는 암호학적 보증을 제공합니다.

## 프라이버시 레벨 이해

| Type     | Prefix   | What It Means                                                                    |
| -------- | -------- | -------------------------------------------------------------------------------- |
| **TEE**  | `tee-*`  | 모델이 하드웨어로 보호된 enclave에서 실행됩니다. Venice는 계산에 접근할 수 없습니다. attestation으로 검증할 수 있습니다. |
| **E2EE** | `e2ee-*` | 완전한 종단간 암호화. prompt는 전송 전 클라이언트에서 암호화됩니다. TEE만이 이를 복호화할 수 있습니다.                  |

<Info>
  E2EE 모델은 TEE 보호와 더불어 클라이언트 측 암호화를 포함합니다. TEE 모델은 클라이언트 측 암호화 없이도 enclave 수준의 보안을 제공합니다.
</Info>

## 사용 가능한 모델

<div id="tee-e2ee-models-placeholder">Loading...</div>

가격 및 context 한도가 포함된 전체 목록은 [Models 페이지](/overview/models)를 확인하세요.

## TEE 모델

TEE 모델은 하드웨어로 보호된 enclave(Intel TDX, NVIDIA Confidential Computing) 안에서 실행됩니다. 모델 가중치와 사용자 데이터는—Venice 인프라를 포함한—호스트 시스템으로부터 보호됩니다.

### 기본 사용법

TEE 모델은 일반 모델과 동일하게 동작합니다:

<CodeGroup>
  ```python Python theme={"system"}
  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)
  ```

  ```javascript Node.js theme={"system"}
  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);
  ```

  ```bash cURL theme={"system"}
  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"}]
    }'
  ```
</CodeGroup>

### TEE Attestation 검증하기

attestation 보고서를 가져와 모델이 진짜 TEE에서 실행되는지 암호학적으로 검증할 수 있습니다:

<CodeGroup>
  ```bash cURL theme={"system"}
  # 무작위 nonce 생성(재전송 공격 방지)
  NONCE=$(openssl rand -hex 16)

  # attestation 가져오기
  curl "https://api.venice.ai/api/v1/tee/attestation?model=tee-qwen3-5-122b-a10b&nonce=$NONCE" \
    -H "Authorization: Bearer $API_KEY_VENICE"
  ```

  ```python Python theme={"system"}
  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']}")
  ```
</CodeGroup>

attestation 응답에는 다음이 포함됩니다:

| Field             | Description                                                    |
| ----------------- | -------------------------------------------------------------- |
| `verified`        | 서버 측 검증이 통과되었는지 여부                                             |
| `nonce`           | 사용자가 보낸 nonce(최신성 확인)                                          |
| `model`           | 검증된 모델 ID                                                      |
| `tee_provider`    | TEE 공급자 식별자                                                    |
| `intel_quote`     | 클라이언트 측 검증용 원시 Intel TDX quote(base64)                         |
| `nvidia_payload`  | NVIDIA GPU attestation 데이터(해당 시)                               |
| `signing_key`     | 응답 서명을 검증하기 위한 공개 키(E2EE 흐름에서 보통 필요. 일부 일반 TEE 모델에서는 생략될 수 있음) |
| `signing_address` | signing key로부터 파생된 이더리움 주소                                     |

<Tip>
  프로덕션에서는 Intel TDX quote를 파싱하고 NVIDIA attestation을 확인해 클라이언트 측에서 attestation을 검증하세요.
</Tip>

<Note>
  일반 TEE 모델 검증의 경우, 기본 attestation 점검에는 `signing_address`와 서버 측 검증 필드만으로도 충분합니다. 클라이언트 측 E2EE 키 합의 및 엄격한 키 바인딩 점검이 필요한 경우에는 `signing_key`가 필요합니다.
</Note>

### 응답 서명

TEE 모델은 응답에 서명할 수 있으며, 이를 통해 결과가 검증된 enclave에서 왔음을 증명할 수 있습니다:

<CodeGroup>
  ```bash cURL theme={"system"}
  # completion을 받은 후 서명을 검증
  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"
  ```

  ```python Python theme={"system"}
  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()
  # 서명이 attestation의 signing_address와 일치하는지 검증
  ```
</CodeGroup>

## E2EE 모델

E2EE 모델은 TEE 보호 위에 클라이언트 측 암호화를 더합니다. prompt는 사용자의 기기를 떠나기 전에 암호화되며, TEE만 이를 복호화할 수 있습니다.

Venice E2EE는 다음을 사용합니다:

* 키 교환을 위한 secp256k1 위 **ECDH(Elliptic Curve Diffie-Hellman)**
* 키 파생을 위한 **HKDF-SHA256**
* 대칭 암호화를 위한 **AES-256-GCM**
* 모델이 안전한 enclave에서 실행됨을 검증하는 **TEE attestation**

<Warning>
  E2EE는 클라이언트 측 구현이 필요합니다. 아래 예시는 전체 프로토콜을 보여줍니다.
</Warning>

### E2EE 동작 방식

<Steps>
  <Step title="임시 키 쌍 생성">
    클라이언트가 세션용 secp256k1 키 쌍을 생성합니다.
  </Step>

  <Step title="TEE Attestation 가져오기">
    클라이언트가 `/api/v1/tee/attestation`을 요청해 모델의 공개 키, attestation 증거, nonce를 받습니다.
  </Step>

  <Step title="Attestation 검증">
    클라이언트가 nonce 일치, 디버그 모드 비활성화, attestation 유효성을 확인합니다.
  </Step>

  <Step title="메시지 암호화">
    클라이언트가 ECDH 공유 비밀 → HKDF → AES-GCM 순으로 prompt를 암호화합니다.
  </Step>

  <Step title="요청 전송">
    클라이언트가 E2EE 헤더(`X-Venice-TEE-Client-Pub-Key`, `X-Venice-TEE-Model-Pub-Key`, `X-Venice-TEE-Signing-Algo`)와 함께 요청을 전송합니다.
  </Step>

  <Step title="TEE 처리">
    TEE가 요청을 복호화하고, 처리한 뒤 응답을 암호화합니다.
  </Step>

  <Step title="응답 복호화">
    클라이언트가 암호화된 청크를 받아 private key로 복호화합니다.
  </Step>
</Steps>

### 사전 요구사항

**JavaScript (Node.js ESM):**

```bash theme={"system"}
npm install elliptic @noble/ciphers @noble/hashes
```

**Python:**

```bash theme={"system"}
pip install cryptography ecdsa requests
```

### 1단계: 모델 E2EE 지원 확인

먼저 `/models` endpoint를 확인해 모델이 E2EE를 지원하는지 확인하세요.

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  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)
  }

  // 사용 예시
  const models = await getE2EEModels('your-api-key')
  console.log('E2EE Models:', models.map(m => m.id))
  // 출력: ['e2ee-qwen3-5-122b-a10b', 'e2ee-glm-5', ...]
  ```

  ```python Python theme={"system"}
  import requests

  def get_e2ee_models(api_key: str) -> list:
      """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')
      ]

  # 사용 예시
  models = get_e2ee_models('your-api-key')
  print('E2EE Models:', [m['id'] for m in models])
  ```
</CodeGroup>

### 2단계: 임시 키 쌍 생성

각 세션마다 새 키 쌍을 생성합니다. private key는 메모리에만 보관하고 사용 후 안전하게 0으로 채워야 합니다.

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  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'), // 비압축 포맷 (65바이트 hex)
    }
  }

  // 보안: 사용 종료 후 private key를 0으로 채우기
  function zeroFill(arr) {
    arr.fill(0)
  }
  ```

  ```python Python theme={"system"}
  from ecdsa import SECP256k1, SigningKey
  import secrets

  def generate_ephemeral_key_pair():
      """E2EE 세션을 위한 임시 secp256k1 키 쌍을 생성합니다."""
      private_key = SigningKey.generate(curve=SECP256k1)
      public_key = private_key.get_verifying_key()

      # 비압축 공개 키 가져오기 (04 || x || y)
      public_key_bytes = b'\x04' + public_key.to_string()

      return {
          'private_key': private_key.to_string(),  # 32 바이트
          'public_key_hex': public_key_bytes.hex()  # 130 hex 문자
      }
  ```
</CodeGroup>

#### 검증 헬퍼

요청을 보내기 전에 키와 암호화된 콘텐츠를 검증하려면 다음 헬퍼 함수를 사용하세요.

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  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) {
    // 최소: ephemeral_pub (65) + nonce (12) + tag (16) = 93 바이트 = 186 hex 문자
    return s.length >= 186 && /^[0-9a-fA-F]+$/.test(s)
  }
  ```

  ```python Python theme={"system"}
  def validate_client_pubkey(pubkey_hex: str) -> None:
      """클라이언트 공개 키 포맷 검증."""
      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:
      """유효한 hex 암호 콘텐츠인지 확인."""
      # 최소: ephemeral_pub (65) + nonce (12) + tag (16) = 93 바이트 = 186 hex 문자
      return len(s) >= 186 and all(c in '0123456789abcdefABCDEF' for c in s)
  ```
</CodeGroup>

### 3단계: TEE Attestation 가져오기 및 검증

attestation은 모델이 진짜 TEE에서 실행되고 있음을 증명합니다. 모델의 공개 키를 신뢰하기 전에 항상 attestation을 검증하세요.

<Info>
  **중요: Nonce 길이** — 클라이언트 nonce는 **32바이트(64 hex 문자)** 여야 합니다. 일부 TEE 공급자는 정확히 32바이트를 요구하며 더 짧은 nonce는 거부합니다.
</Info>

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  import crypto from 'crypto'

  async function fetchAndVerifyAttestation(modelId, apiKey) {
    // 재전송 방지를 위한 클라이언트 nonce 생성 (32바이트 = 64 hex 문자)
    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 검증
    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')
    }

    // 암호화용 모델 공개 키 가져오기
    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,
    }
  }
  ```

  ```python Python theme={"system"}
  import secrets
  import requests

  def fetch_and_verify_attestation(model_id: str, api_key: str) -> dict:
      """모델에 대한 TEE attestation을 가져오고 검증합니다."""
      # 재전송 방지를 위한 클라이언트 nonce 생성 (32바이트 = 64 hex 문자)
      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 검증
      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')

      # 암호화용 모델 공개 키 가져오기
      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
      }
  ```
</CodeGroup>

### 4단계: 메시지 암호화

전송 전에 user 및 system 메시지를 암호화합니다. `user`와 `system` 역할 메시지만 암호화하면 됩니다.

<Warning>
  E2EE 헤더가 있는 경우, **모든** `user` 및 `system` 역할 메시지는 암호화되어야 합니다. 이 역할에 평문 콘텐츠를 보내면 "Encrypted field is not valid hex" 에러가 발생합니다.
</Warning>

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  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')

    // 공개 키 정규화 (필요 시 04 prefix 추가)
    let normalizedKey = modelPublicKeyHex
    if (!normalizedKey.startsWith('04') && normalizedKey.length === 128) {
      normalizedKey = '04' + normalizedKey
    }

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

    // 이 메시지를 위한 임시 키 쌍 생성
    const ephemeralKeyPair = ec.genKeyPair()

    // ECDH 공유 비밀
    const sharedSecret = ephemeralKeyPair.derive(modelPublicKey.getPublic())
    const sharedSecretBytes = new Uint8Array(sharedSecret.toArray('be', 32))

    // HKDF로 AES 키 파생
    const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

    // 무작위 nonce 생성
    const nonce = crypto.randomBytes(12)

    // AES-GCM 으로 암호화
    const cipher = gcm(aesKey, nonce)
    const encrypted = cipher.encrypt(new TextEncoder().encode(plaintext))

    // 임시 공개 키 가져오기 (비압축)
    const ephemeralPublic = new Uint8Array(ephemeralKeyPair.getPublic(false, 'array'))

    // 결합: ephemeral_public (65바이트) + nonce (12바이트) + 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
    })
  }
  ```

  ```python Python theme={"system"}
  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:
      """ECDH + HKDF + AES-GCM을 사용해 메시지를 암호화."""
      # 공개 키 정규화
      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)

      # 모델의 공개 키 파싱 (04 prefix 생략)
      model_verifying_key = VerifyingKey.from_string(
          model_public_key_bytes[1:],
          curve=SECP256k1
      )

      # 이 메시지를 위한 임시 키 쌍 생성
      ephemeral_private = SigningKey.generate(curve=SECP256k1)
      ephemeral_public = ephemeral_private.get_verifying_key()

      # ECDH: 공유 비밀 계산
      shared_point = model_verifying_key.pubkey.point * ephemeral_private.privkey.secret_multiplier
      shared_secret = shared_point.x().to_bytes(32, 'big')

      # HKDF로 AES 키 파생
      hkdf = HKDF(
          algorithm=hashes.SHA256(),
          length=32,
          salt=None,
          info=HKDF_INFO,
      )
      aes_key = hkdf.derive(shared_secret)

      # 무작위 nonce 생성
      nonce = os.urandom(12)

      # AES-GCM 으로 암호화
      aesgcm = AESGCM(aes_key)
      ciphertext = aesgcm.encrypt(nonce, plaintext.encode('utf-8'), None)

      # 임시 공개 키 가져오기 (비압축: 04 || x || y)
      ephemeral_public_bytes = b'\x04' + ephemeral_public.to_string()

      # 결합: ephemeral_public (65바이트) + nonce (12바이트) + ciphertext
      result = ephemeral_public_bytes + nonce + ciphertext

      return result.hex()

  def encrypt_messages_for_e2ee(messages: list, model_public_key: str) -> list:
      """user 및 system 메시지를 암호화."""
      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
  ```
</CodeGroup>

### 5단계: E2EE 헤더와 함께 요청 전송

E2EE 처리를 활성화하려면 필수 헤더를 포함하세요.

| Header                        | Description                     |
| ----------------------------- | ------------------------------- |
| `X-Venice-TEE-Client-Pub-Key` | 클라이언트의 임시 공개 키(비압축 hex, 130 문자) |
| `X-Venice-TEE-Model-Pub-Key`  | attestation으로 받은 모델 공개 키        |
| `X-Venice-TEE-Signing-Algo`   | 항상 `ecdsa`                      |

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  async function sendE2EERequest(messages, model, e2eeContext, apiKey) {
    // 메시지 암호화
    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 헤더
        '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는 스트리밍이 필요
      }),
    })

    return response
  }
  ```

  ```python Python theme={"system"}
  import requests

  def send_e2ee_request(
      messages: list,
      model: str,
      e2ee_context: dict,
      api_key: str
  ) -> requests.Response:
      """E2EE 암호화 chat completion 요청을 전송."""
      # 메시지 암호화
      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 헤더
              '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는 스트리밍이 필요
          },
          stream=True
      )

      return response
  ```
</CodeGroup>

### 6단계: 응답 청크 복호화

E2EE 모델의 응답은 hex 인코딩된 암호화 청크입니다. 각 청크를 private key로 복호화하세요.

<CodeGroup>
  ```javascript JavaScript theme={"system"}
  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) {
    // 최소: ephemeral_pub (65) + nonce (12) + tag (16) = 93 바이트 = 186 hex 문자
    if (s.length < 186) return false
    return /^[0-9a-fA-F]+$/.test(s)
  }

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

    // 구성요소 파싱
    const serverEphemeralPubKey = raw.slice(0, 65)
    const nonce = raw.slice(65, 65 + 12)
    const ciphertext = raw.slice(65 + 12)

    // 서버 임시 키와 ECDH
    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 키 파생
    const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

    // 복호화
    const cipher = gcm(aesKey, nonce)
    const plaintext = cipher.decrypt(ciphertext)

    return new TextDecoder().decode(plaintext)
  }

  // 스트리밍 응답 처리
  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) // 실시간 출력
          } else if (content) {
            fullContent += content
            process.stdout.write(content)
          }
        } catch (e) {
          // 비정상 청크 건너뛰기
        }
      }
    }

    return fullContent
  }
  ```

  ```python Python theme={"system"}
  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:
      """hex 암호화된 콘텐츠처럼 보이는지 확인."""
      if len(s) < 186:  # 최소: 65 + 12 + 16 = 93 바이트 = 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:
      """E2EE 응답 청크를 복호화."""
      raw = bytes.fromhex(ciphertext_hex)

      # 구성요소 파싱
      server_ephemeral_pub = raw[:65]
      nonce = raw[65:77]
      ciphertext = raw[77:]

      # 서버 임시 공개 키 파싱 (04 prefix 생략)
      server_verifying_key = VerifyingKey.from_string(
          server_ephemeral_pub[1:],
          curve=SECP256k1
      )

      # 클라이언트의 private key 재구성
      client_signing_key = SigningKey.from_string(client_private_key, curve=SECP256k1)

      # ECDH: 공유 비밀 계산
      shared_point = server_verifying_key.pubkey.point * client_signing_key.privkey.secret_multiplier
      shared_secret = shared_point.x().to_bytes(32, 'big')

      # AES 키 파생
      hkdf = HKDF(
          algorithm=hashes.SHA256(),
          length=32,
          salt=None,
          info=HKDF_INFO,
      )
      aes_key = hkdf.derive(shared_secret)

      # 복호화
      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:
      """스트리밍 E2EE 응답을 처리."""
      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)  # 실시간 출력
              elif content:
                  full_content += content
                  print(content, end='', flush=True)
          except json.JSONDecodeError:
              pass

      print()  # 마지막 줄바꿈
      return full_content
  ```
</CodeGroup>

### 완전한 동작 예시

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={"system"}
    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() {
      // 1단계: 임시 키 쌍 생성
      console.log('🔑 Generating ephemeral key pair...');
      const ec = new EC('secp256k1');
      const keyPair = ec.genKeyPair();
      const clientPublicKeyHex = keyPair.getPublic('hex');

      // 2단계: attestation 가져오기 및 검증
      console.log('🔍 Fetching TEE attestation...');
      const clientNonce = crypto.randomBytes(32).toString('hex'); // 32바이트 필수
      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');

      // 3단계: 메시지 암호화
      console.log('🔐 Encrypting message...');
      const plaintext = 'What is 2+2? Answer briefly.';

      // 모델 공개 키 정규화 및 파싱
      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 }];

      // 4단계: E2EE 요청 전송
      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 }),
      });

      // 5단계: 응답 복호화
      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) {
              // 복호화
              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);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    #!/usr/bin/env python3
    """Venice AI API의 완전한 E2EE 구현 예시."""

    import os
    import json
    import secrets
    import requests
    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

    API_KEY = os.environ.get('API_KEY_VENICE')
    BASE_URL = 'https://api.venice.ai/api/v1'
    MODEL = 'e2ee-qwen3-5-122b-a10b'
    HKDF_INFO = b'ecdsa_encryption'

    def main():
        # 1단계: 임시 키 쌍 생성
        print('🔑 Generating ephemeral key pair...')
        private_key = SigningKey.generate(curve=SECP256k1)
        public_key = private_key.get_verifying_key()
        client_public_key_hex = (b'\x04' + public_key.to_string()).hex()

        # 2단계: attestation 가져오기 및 검증
        print('🔍 Fetching TEE attestation...')
        client_nonce = secrets.token_hex(32)  # 32바이트 필수
        attestation_res = requests.get(
            f'{BASE_URL}/tee/attestation',
            params={'model': MODEL, 'nonce': client_nonce},
            headers={'Authorization': f'Bearer {API_KEY}'},
            timeout=30
        )
        attestation = attestation_res.json()

        if attestation.get('verified') != True or attestation.get('nonce') != client_nonce:
            raise ValueError('Attestation verification failed')

        model_public_key = attestation.get('signing_key') or attestation.get('signing_public_key')
        print(f'✅ TEE attestation verified (provider: {attestation.get("tee_provider", "unknown")})')

        # 3단계: 메시지 암호화
        print('🔐 Encrypting message...')
        plaintext = 'What is 2+2? Answer briefly.'

        # 공개 키 정규화
        key_hex = model_public_key
        if not key_hex.startswith('04') and len(key_hex) == 128:
            key_hex = '04' + key_hex

        model_key_bytes = bytes.fromhex(key_hex)
        model_verifying_key = VerifyingKey.from_string(model_key_bytes[1:], curve=SECP256k1)

        # ECDH
        ephemeral_private = SigningKey.generate(curve=SECP256k1)
        ephemeral_public = ephemeral_private.get_verifying_key()
        shared_point = model_verifying_key.pubkey.point * ephemeral_private.privkey.secret_multiplier
        shared_secret = shared_point.x().to_bytes(32, 'big')

        # AES 키 파생
        hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=HKDF_INFO)
        aes_key = hkdf.derive(shared_secret)

        # 암호화
        nonce = os.urandom(12)
        aesgcm = AESGCM(aes_key)
        ciphertext = aesgcm.encrypt(nonce, plaintext.encode('utf-8'), None)

        ephemeral_public_bytes = b'\x04' + ephemeral_public.to_string()
        result = ephemeral_public_bytes + nonce + ciphertext
        encrypted_content = result.hex()

        messages = [{'role': 'user', 'content': encrypted_content}]

        # 4단계: E2EE 요청 전송
        print('📤 Sending encrypted request...')
        response = requests.post(
            f'{BASE_URL}/chat/completions',
            headers={
                'Authorization': f'Bearer {API_KEY}',
                'Content-Type': 'application/json',
                'X-Venice-TEE-Client-Pub-Key': client_public_key_hex,
                'X-Venice-TEE-Model-Pub-Key': model_public_key,
                'X-Venice-TEE-Signing-Algo': 'ecdsa'
            },
            json={'model': MODEL, 'messages': messages, 'stream': True},
            stream=True,
            timeout=60
        )

        # 5단계: 응답 복호화
        print('📥 Decrypting response...\n')

        for line in response.iter_lines():
            if not line:
                continue
            line_str = line.decode('utf-8')
            if not line_str.startswith('data: ') or '[DONE]' in line_str:
                continue

            try:
                chunk = json.loads(line_str[6:])
                content = chunk.get('choices', [{}])[0].get('delta', {}).get('content', '')
                if not content:
                    continue

                # 암호화 여부 확인
                if len(content) >= 186 and all(c in '0123456789abcdefABCDEF' for c in content):
                    raw = bytes.fromhex(content)
                    server_ephemeral_pub = raw[:65]
                    nonce = raw[65:77]
                    ciphertext = raw[77:]

                    server_verifying_key = VerifyingKey.from_string(server_ephemeral_pub[1:], curve=SECP256k1)
                    shared_point = server_verifying_key.pubkey.point * private_key.privkey.secret_multiplier
                    shared_secret = shared_point.x().to_bytes(32, 'big')

                    hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=HKDF_INFO)
                    aes_key = hkdf.derive(shared_secret)

                    aesgcm = AESGCM(aes_key)
                    plaintext = aesgcm.decrypt(nonce, ciphertext, None)
                    print(plaintext.decode('utf-8'), end='', flush=True)
                else:
                    print(content, end='', flush=True)
            except Exception:
                pass

        print('\n\n🔐 Response decrypted end-to-end')

    if __name__ == '__main__':
        main()
    ```
  </Tab>
</Tabs>

### E2EE 제한 사항

<Warning>
  암호화 요건으로 인해 E2EE에는 몇 가지 제약이 있습니다:
</Warning>

| Feature              | Status                     |
| -------------------- | -------------------------- |
| Streaming            | **필수**(비스트리밍 미지원)          |
| Web search           | **비활성화**(콘텐츠 유출 가능성)       |
| File uploads         | **미지원**                    |
| Function calling     | **미지원**                    |
| Venice system prompt | **비활성화**(클라이언트에서 암호화되어야 함) |

### 보안 모범 사례

1. **세션마다 새 키 쌍을 생성하세요** - 임시 키를 재사용하지 마세요
2. **Private key를 0으로 채우세요** - 사용 후 메모리에서 private key 바이트를 지우세요
3. **Attestation을 검증하세요** - 항상 `verified: true`와 nonce 일치를 확인하세요
4. **디버그 모드 확인** - 디버그 enclave에서 온 attestation은 거부하세요
5. **스트리밍 사용** - E2EE는 적절한 암호화 청킹을 위해 스트리밍이 필요합니다
6. **에러를 우아하게 처리** - 사용자에게 복호화 에러를 노출하지 마세요
7. **32바이트 nonce 사용** - TEE 공급자는 정확히 32바이트를 요구합니다

## 모범 사례

<AccordionGroup>
  <Accordion title="프로덕션에서는 항상 attestation을 검증하세요">
    `verified: true` 응답만 믿지 마세요. Intel TDX quote를 클라이언트 측에서 파싱하고 측정값이 기대값과 일치하는지 검증하세요. NVIDIA GPU의 경우 NVIDIA 검증 서비스를 통해 attestation을 확인하세요.
  </Accordion>

  <Accordion title="새 nonce를 사용하세요">
    매 attestation 요청마다 새 무작위 nonce를 생성하세요. 이로써 공격자가 오래된 attestation을 제공하는 재전송 공격을 방지할 수 있습니다.
  </Accordion>

  <Accordion title="키 바인딩을 확인하세요">
    signing key는 TDX REPORTDATA 필드에 바인딩되어야 합니다. 이는 키가 enclave 내부에서 생성되었음을 증명합니다.
  </Accordion>

  <Accordion title="디버그 모드 확인">
    TDX attestation에 디버그 플래그가 설정되어 있지 않은지 확인하세요. 디버그 enclave는 검사가 가능하므로 프로덕션에서는 신뢰해서는 안 됩니다.
  </Accordion>

  <Accordion title="E2EE에는 공식 SDK를 사용하세요">
    E2EE는 신중한 암호 구현이 필요합니다. 직접 프로토콜을 구현하기보다 공식 SDK를 사용하세요.
  </Accordion>
</AccordionGroup>

## 모델 기능 확인

다음과 같이 models endpoint를 통해 모델이 TEE 또는 E2EE를 지원하는지 확인할 수 있습니다:

<CodeGroup>
  ```bash cURL theme={"system"}
  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}'
  ```

  ```python Python theme={"system"}
  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')}")
  ```
</CodeGroup>

## 에러 처리

| Error                                 | Cause             | Solution                 |
| ------------------------------------- | ----------------- | ------------------------ |
| `TEE attestation verification failed` | attestation 검증 실패 | 재시도하거나 지원에 문의            |
| `Attestation nonce mismatch`          | 재전송 공격 가능성        | 새 nonce 생성               |
| `TDX debug mode detected`             | enclave가 디버그 모드   | 프로덕션에서 사용 금지             |
| `Failed to decrypt field`             | 서버 측 E2EE 복호화 실패  | 암호화 구현 확인                |
| `E2EE requires streaming`             | E2EE 모델에 비스트리밍 요청 | `stream: true` 설정        |
| `Encrypted field is not valid hex`    | E2EE 헤더와 함께 평문 전송 | 모든 user/system 메시지를 암호화  |
| `Invalid public key`                  | 잘못된 키 포맷          | `04`로 시작하는 130 hex 문자 사용 |

## 문제 해결

<AccordionGroup>
  <Accordion title="502 Bad Gateway 또는 'Nonce must be exactly 32 bytes'">
    nonce 길이가 잘못되었습니다. TEE 공급자는 정확히 **32바이트(64 hex 문자)** 를 요구합니다.

    * `crypto.randomBytes(32).toString('hex')` (JS) 또는 `secrets.token_hex(32)` (Python) 사용
    * 흔한 실수: `secrets.token_hex(16)`은 32 hex 문자(16바이트)를 생성하며 32바이트가 아닙니다
  </Accordion>

  <Accordion title="Attestation 검증 실패">
    * 모델이 E2EE를 지원하는지 확인 (`supportsE2EE: true`)
    * API 키가 유효하고 요청한 모델에 접근 권한이 있는지 확인
    * Venice API와의 네트워크 연결 확인
  </Accordion>

  <Accordion title="복호화 실패">
    * 헤더로 전송한 공개 키를 생성한 동일한 private key를 사용하고 있는지 확인
    * 응답 콘텐츠가 실제로 hex 인코딩되어 있는지 확인(E2EE 활성)
    * 모델 공개 키가 암호화에 사용된 것과 일치하는지 확인
  </Accordion>

  <Accordion title="Encrypted field is not valid hex">
    * E2EE 헤더가 있을 때 모든 `user` 및 `system` 역할 메시지는 암호화되어야 합니다
    * 암호화된 콘텐츠가 `isValidEncrypted()` 검증을 통과하는지 확인(최소 186 hex 문자)
    * 암호화 결과가 prefix 없는 소문자 hex인지 확인
  </Accordion>

  <Accordion title="Invalid public key 에러">
    * 클라이언트 공개 키는 `04`로 시작하는 정확히 **130 hex 문자** 여야 합니다
    * 전송 전에 `validateClientPubkey()` 헬퍼로 포맷을 확인
    * 비압축 공개 키 포맷(65바이트 = 130 hex 문자) 사용 확인
  </Accordion>

  <Accordion title="모델을 찾을 수 없습니다">
    * 모델 ID가 정확하고 모델이 E2EE를 지원하는지 확인
    * `/models` endpoint로 사용 가능한 E2EE 모델을 확인
  </Accordion>
</AccordionGroup>

## 리소스

* [Intel TDX 문서](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/documentation.html)
* [NVIDIA Confidential Computing](https://developer.nvidia.com/confidential-computing)
