跳转到主要内容
Venice 提供运行在可信执行环境(TEE)中并支持端到端加密(E2EE)的隐私增强模型。这些模型为您的数据保密提供了密码学保证——即使是 Venice 也无法访问。

了解隐私级别

类型前缀含义
TEEtee-*模型在硬件安全 enclave 中运行。Venice 无法访问运算过程。您可以通过证明(attestation)验证。
E2EEe2ee-*完整的端到端加密。您的 prompt 在发送前会在客户端加密。只有 TEE 能够解密。
E2EE 模型在 TEE 保护之上加入了客户端加密。TEE 模型提供 enclave 安全性,无需客户端加密。

可用模型

Loading…
查看模型页面获取包含定价和上下文限制的完整列表。

TEE 模型

TEE 模型在硬件安全 enclave(Intel TDX、NVIDIA Confidential Computing)中运行。模型权重和您的数据受到保护,不会被主机系统访问——包括 Venice 的基础设施。

基础用法

TEE 模型的使用方式与常规模型完全相同:
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)

验证 TEE 证明

您可以通过获取模型的证明报告,以密码学方式验证模型正在真实的 TEE 中运行:
# Generate a random nonce (prevents replay attacks)
NONCE=$(openssl rand -hex 16)

# Fetch attestation
curl "https://api.venice.ai/api/v1/tee/attestation?model=tee-qwen3-5-122b-a10b&nonce=$NONCE" \
  -H "Authorization: Bearer $API_KEY_VENICE"
证明响应包含:
字段说明
verified证明是否通过了服务端验证
nonce您的 nonce,用于确认新鲜性
model被证明的模型 ID
tee_providerTEE 提供商标识符
intel_quote用于客户端验证的原始 Intel TDX quote(base64)
nvidia_payloadNVIDIA GPU 证明数据(如适用)
signing_key用于验证响应签名的公钥(通常 E2EE 流程必需;某些纯 TEE 模型可省略)
signing_address从签名密钥派生的以太坊地址
在生产用途中,请在客户端通过解析 Intel TDX quote 并检查 NVIDIA 证明来验证证明。
对于纯 TEE 模型验证,signing_address 和服务端验证字段足以进行基础证明检查。当您需要客户端 E2EE 密钥协商和严格的密钥绑定检查时,需要 signing_key

响应签名

TEE 模型可以对其响应进行签名,证明输出来自被证明的 enclave:
# After getting a completion, verify the signature
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"

E2EE 模型

E2EE 模型在 TEE 保护之上增加了客户端加密。您的 prompt 在离开设备之前被加密,只有 TEE 能够解密。 Venice E2EE 使用:
  • ECDH(椭圆曲线 Diffie-Hellman) 基于 secp256k1 进行密钥交换
  • HKDF-SHA256 用于密钥派生
  • AES-256-GCM 用于对称加密
  • TEE 证明用于验证模型运行在安全 enclave 中
E2EE 需要客户端实现。下面的示例展示了完整协议。

E2EE 的工作方式

1

生成临时密钥对

客户端为此会话生成 secp256k1 密钥对。
2

获取 TEE 证明

客户端请求 /api/v1/tee/attestation 并接收模型的公钥、证明证据和 nonce。
3

验证证明

客户端检查 nonce 匹配、debug 模式被禁用以及证明有效性。
4

加密消息

客户端使用 ECDH 共享秘密 → HKDF → AES-GCM 加密 prompt。
5

发送请求

客户端发送带 E2EE 头部(X-Venice-TEE-Client-Pub-KeyX-Venice-TEE-Model-Pub-KeyX-Venice-TEE-Signing-Algo)的请求。
6

TEE 处理

TEE 解密请求、处理它并加密响应。
7

解密响应

客户端接收加密的 chunk 并使用私钥解密。

前置条件

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

步骤 1:检查模型 E2EE 支持

首先,通过检查 /models 端点验证模型是否支持 E2EE。
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)
}

// Example usage
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', ...]

步骤 2:生成临时密钥对

为每个会话生成新的密钥对。私钥应仅保留在内存中,并在使用后安全清零。
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'), // Uncompressed format (65 bytes hex)
  }
}

// Security: Zero-fill private key when done
function zeroFill(arr) {
  arr.fill(0)
}

验证辅助函数

在发送请求之前使用这些辅助函数验证密钥和加密内容。
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 chars
  return s.length >= 186 && /^[0-9a-fA-F]+$/.test(s)
}

步骤 3:获取并验证 TEE 证明

证明证明模型正在真实的 TEE 中运行。在信任模型的公钥之前,请始终验证证明。
重要:Nonce 长度 - 客户端 nonce 必须是 32 字节(64 个十六进制字符)。某些 TEE 提供商要求恰好 32 字节,并会拒绝更短的 nonce。
import crypto from 'crypto'

async function fetchAndVerifyAttestation(modelId, apiKey) {
  // Generate client nonce for replay protection (32 bytes = 64 hex chars)
  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()

  // Verify 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')
  }

  // Get model's public key for encryption
  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,
  }
}

步骤 4:加密消息

在发送之前加密用户和系统消息。只有 usersystem 角色的消息需要加密。
当存在 E2EE 头部时,所有 usersystem 角色的消息都必须加密。在这些角色中发送任何明文内容将导致 “Encrypted field is not valid hex” 错误。
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')

  // Normalize public key (add 04 prefix if needed)
  let normalizedKey = modelPublicKeyHex
  if (!normalizedKey.startsWith('04') && normalizedKey.length === 128) {
    normalizedKey = '04' + normalizedKey
  }

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

  // Generate ephemeral key pair for this message
  const ephemeralKeyPair = ec.genKeyPair()

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

  // Derive AES key using HKDF
  const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

  // Generate random nonce
  const nonce = crypto.randomBytes(12)

  // Encrypt with AES-GCM
  const cipher = gcm(aesKey, nonce)
  const encrypted = cipher.encrypt(new TextEncoder().encode(plaintext))

  // Get ephemeral public key (uncompressed)
  const ephemeralPublic = new Uint8Array(ephemeralKeyPair.getPublic(false, 'array'))

  // Combine: 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
  })
}

步骤 5:使用 E2EE 头部发送请求

包含所需的头部以启用 E2EE 处理。
Header说明
X-Venice-TEE-Client-Pub-Key您的临时公钥(未压缩十六进制,130 个字符)
X-Venice-TEE-Model-Pub-Key来自证明的模型公钥
X-Venice-TEE-Signing-Algo始终为 ecdsa
async function sendE2EERequest(messages, model, e2eeContext, apiKey) {
  // Encrypt messages
  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 headers
      '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 requires streaming
    }),
  })

  return response
}

步骤 6:解密响应 chunk

来自 E2EE 模型的响应是十六进制编码的加密 chunk。使用您的私钥解密每个 chunk。
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 chars
  if (s.length < 186) return false
  return /^[0-9a-fA-F]+$/.test(s)
}

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

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

  // ECDH with server's ephemeral key
  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))

  // Derive AES key
  const aesKey = hkdf(sha256, sharedSecretBytes, undefined, HKDF_INFO, 32)

  // Decrypt
  const cipher = gcm(aesKey, nonce)
  const plaintext = cipher.decrypt(ciphertext)

  return new TextDecoder().decode(plaintext)
}

// Process streaming response
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) // Real-time output
        } else if (content) {
          fullContent += content
          process.stdout.write(content)
        }
      } catch (e) {
        // Skip malformed chunks
      }
    }
  }

  return fullContent
}

完整工作示例

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() {
  // Step 1: Generate ephemeral key pair
  console.log('🔑 Generating ephemeral key pair...');
  const ec = new EC('secp256k1');
  const keyPair = ec.genKeyPair();
  const clientPublicKeyHex = keyPair.getPublic('hex');

  // Step 2: Fetch and verify attestation
  console.log('🔍 Fetching TEE attestation...');
  const clientNonce = crypto.randomBytes(32).toString('hex'); // 32 bytes required
  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');

  // Step 3: Encrypt message
  console.log('🔐 Encrypting message...');
  const plaintext = 'What is 2+2? Answer briefly.';

  // Normalize and parse model's public key
  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 }];

  // Step 4: Send E2EE request
  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 }),
  });

  // Step 5: Decrypt response
  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) {
          // Decrypt
          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 限制

由于加密要求,E2EE 存在一些限制:
功能状态
Streaming必需(不支持非流式)
Web search禁用(会泄露内容)
文件上传不支持
Function calling不支持
Venice 系统 prompt禁用(必须在客户端加密)

安全最佳实践

  1. 每个会话生成新的密钥对 - 不要复用临时密钥
  2. 清零私钥 - 使用完成后从内存中清除私钥字节
  3. 验证证明 - 始终检查 verified: true 和 nonce 匹配
  4. 检查 debug 模式 - 拒绝来自 debug enclave 的证明
  5. 使用流式传输 - E2EE 需要流式传输以正确分块加密
  6. 优雅处理错误 - 不要向用户暴露解密错误
  7. 使用 32 字节 nonce - TEE 提供商要求恰好 32 字节

最佳实践

不要仅信任 verified: true 响应。在客户端解析 Intel TDX quote 并验证测量值是否与预期值匹配。对于 NVIDIA GPU,通过 NVIDIA 的验证服务检查证明。
始终为每个证明请求生成新的随机 nonce。这可以防止攻击者提供过时证明的重放攻击。
签名密钥应该绑定到 TDX REPORTDATA 字段。这证明密钥是在 enclave 内部生成的。
验证 TDX 证明没有设置 debug 标志。debug enclave 可以被检查,不应被信任用于生产环境。
E2EE 需要谨慎的密码学实现。请使用我们的官方 SDK 而不是自己实现协议。

检查模型能力

您可以通过 models 端点检查模型是否支持 TEE 或 E2EE:
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}'

错误处理

错误原因解决方案
TEE attestation verification failed证明未通过验证重试或联系支持
Attestation nonce mismatch可能的重放攻击生成新的 nonce
TDX debug mode detectedenclave 处于 debug 模式不要用于生产环境
Failed to decrypt fieldE2EE 解密在服务端失败检查您的加密实现
E2EE requires streaming对 E2EE 模型发送非流式请求设置 stream: true
Encrypted field is not valid hex与 E2EE 头部一起发送了明文加密所有 user/system 消息
Invalid public key错误的密钥格式使用 130 个十六进制字符并以 04 开头

故障排查

nonce 长度不正确。TEE 提供商要求恰好 32 字节(64 个十六进制字符)
  • 使用 crypto.randomBytes(32).toString('hex')(JS)或 secrets.token_hex(32)(Python)
  • 常见错误:secrets.token_hex(16) 产生 32 个十六进制字符(16 字节),而非 32 字节
  • 检查模型是否支持 E2EE(supportsE2EE: true
  • 验证您的 API 密钥有效且可访问请求的模型
  • 验证到 Venice API 的网络连接
  • 确保您使用的是生成头部中发送的公钥的相同私钥
  • 检查响应内容是否确实是十六进制编码(E2EE 处于激活状态)
  • 验证模型公钥与用于加密的密钥匹配
  • 当存在 E2EE 头部时,所有 usersystem 角色的消息都必须加密
  • 验证您的加密内容通过 isValidEncrypted() 验证(最少 186 个十六进制字符)
  • 检查加密输出是小写十六进制且没有任何前缀
  • 客户端公钥必须恰好是以 04 开头的 130 个十六进制字符
  • 使用 validateClientPubkey() 辅助函数在发送前验证格式
  • 确保您使用未压缩的公钥格式(65 字节 = 130 个十六进制字符)
  • 验证模型 ID 正确且模型支持 E2EE
  • 使用 /models 端点验证可用的 E2EE 模型

资源