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만이 이를 복호화할 수 있습니다.
E2EE 모델은 TEE 보호와 더불어 클라이언트 측 암호화를 포함합니다. TEE 모델은 클라이언트 측 암호화 없이도 enclave 수준의 보안을 제공합니다.
사용 가능한 모델
Loading…
가격 및 context 한도가 포함된 전체 목록은 Models 페이지 를 확인하세요.
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 Attestation 검증하기
attestation 보고서를 가져와 모델이 진짜 TEE에서 실행되는지 암호학적으로 검증할 수 있습니다:
# 무작위 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 "
attestation 응답에는 다음이 포함됩니다:
Field Description verified서버 측 검증이 통과되었는지 여부 nonce사용자가 보낸 nonce(최신성 확인) model검증된 모델 ID tee_providerTEE 공급자 식별자 intel_quote클라이언트 측 검증용 원시 Intel TDX quote(base64) nvidia_payloadNVIDIA GPU attestation 데이터(해당 시) signing_key응답 서명을 검증하기 위한 공개 키(E2EE 흐름에서 보통 필요. 일부 일반 TEE 모델에서는 생략될 수 있음) signing_addresssigning key로부터 파생된 이더리움 주소
프로덕션에서는 Intel TDX quote를 파싱하고 NVIDIA attestation을 확인해 클라이언트 측에서 attestation을 검증하세요.
일반 TEE 모델 검증의 경우, 기본 attestation 점검에는 signing_address와 서버 측 검증 필드만으로도 충분합니다. 클라이언트 측 E2EE 키 합의 및 엄격한 키 바인딩 점검이 필요한 경우에는 signing_key가 필요합니다.
응답 서명
TEE 모델은 응답에 서명할 수 있으며, 이를 통해 결과가 검증된 enclave에서 왔음을 증명할 수 있습니다:
# 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 "
E2EE 모델
E2EE 모델은 TEE 보호 위에 클라이언트 측 암호화를 더합니다. prompt는 사용자의 기기를 떠나기 전에 암호화되며, TEE만 이를 복호화할 수 있습니다.
Venice E2EE는 다음을 사용합니다:
키 교환을 위한 secp256k1 위 ECDH(Elliptic Curve Diffie-Hellman)
키 파생을 위한 HKDF-SHA256
대칭 암호화를 위한 AES-256-GCM
모델이 안전한 enclave에서 실행됨을 검증하는 TEE attestation
E2EE는 클라이언트 측 구현이 필요합니다. 아래 예시는 전체 프로토콜을 보여줍니다.
E2EE 동작 방식
임시 키 쌍 생성
클라이언트가 세션용 secp256k1 키 쌍을 생성합니다.
TEE Attestation 가져오기
클라이언트가 /api/v1/tee/attestation을 요청해 모델의 공개 키, attestation 증거, nonce를 받습니다.
Attestation 검증
클라이언트가 nonce 일치, 디버그 모드 비활성화, attestation 유효성을 확인합니다.
메시지 암호화
클라이언트가 ECDH 공유 비밀 → HKDF → AES-GCM 순으로 prompt를 암호화합니다.
요청 전송
클라이언트가 E2EE 헤더(X-Venice-TEE-Client-Pub-Key, X-Venice-TEE-Model-Pub-Key, X-Venice-TEE-Signing-Algo)와 함께 요청을 전송합니다.
TEE 처리
TEE가 요청을 복호화하고, 처리한 뒤 응답을 암호화합니다.
응답 복호화
클라이언트가 암호화된 청크를 받아 private key로 복호화합니다.
사전 요구사항
JavaScript (Node.js ESM):
npm install elliptic @noble/ciphers @noble/hashes
Python:
pip install cryptography ecdsa requests
1단계: 모델 E2EE 지원 확인
먼저 /models endpoint를 확인해 모델이 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 )
}
// 사용 예시
const models = await getE2EEModels ( 'your-api-key' )
console . log ( 'E2EE Models:' , models . map ( m => m . id ))
// 출력: ['e2ee-qwen3-5-122b-a10b', 'e2ee-glm-5', ...]
2단계: 임시 키 쌍 생성
각 세션마다 새 키 쌍을 생성합니다. private key는 메모리에만 보관하고 사용 후 안전하게 0으로 채워야 합니다.
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 )
}
검증 헬퍼
요청을 보내기 전에 키와 암호화된 콘텐츠를 검증하려면 다음 헬퍼 함수를 사용하세요.
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 )
}
3단계: TEE Attestation 가져오기 및 검증
attestation은 모델이 진짜 TEE에서 실행되고 있음을 증명합니다. 모델의 공개 키를 신뢰하기 전에 항상 attestation을 검증하세요.
중요: Nonce 길이 — 클라이언트 nonce는 32바이트(64 hex 문자) 여야 합니다. 일부 TEE 공급자는 정확히 32바이트를 요구하며 더 짧은 nonce는 거부합니다.
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 ,
}
}
4단계: 메시지 암호화
전송 전에 user 및 system 메시지를 암호화합니다. user와 system 역할 메시지만 암호화하면 됩니다.
E2EE 헤더가 있는 경우, 모든 user 및 system 역할 메시지는 암호화되어야 합니다. 이 역할에 평문 콘텐츠를 보내면 “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' )
// 공개 키 정규화 (필요 시 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
})
}
5단계: E2EE 헤더와 함께 요청 전송
E2EE 처리를 활성화하려면 필수 헤더를 포함하세요.
Header Description X-Venice-TEE-Client-Pub-Key클라이언트의 임시 공개 키(비압축 hex, 130 문자) X-Venice-TEE-Model-Pub-Keyattestation으로 받은 모델 공개 키 X-Venice-TEE-Signing-Algo항상 ecdsa
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
}
6단계: 응답 청크 복호화
E2EE 모델의 응답은 hex 인코딩된 암호화 청크입니다. 각 청크를 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 ) {
// 최소: 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
}
완전한 동작 예시
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 );
#!/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()
E2EE 제한 사항
암호화 요건으로 인해 E2EE에는 몇 가지 제약이 있습니다:
Feature Status Streaming 필수 (비스트리밍 미지원)Web search 비활성화 (콘텐츠 유출 가능성)File uploads 미지원 Function calling 미지원 Venice system prompt 비활성화 (클라이언트에서 암호화되어야 함)
보안 모범 사례
세션마다 새 키 쌍을 생성하세요 - 임시 키를 재사용하지 마세요
Private key를 0으로 채우세요 - 사용 후 메모리에서 private key 바이트를 지우세요
Attestation을 검증하세요 - 항상 verified: true와 nonce 일치를 확인하세요
디버그 모드 확인 - 디버그 enclave에서 온 attestation은 거부하세요
스트리밍 사용 - E2EE는 적절한 암호화 청킹을 위해 스트리밍이 필요합니다
에러를 우아하게 처리 - 사용자에게 복호화 에러를 노출하지 마세요
32바이트 nonce 사용 - TEE 공급자는 정확히 32바이트를 요구합니다
모범 사례
프로덕션에서는 항상 attestation을 검증하세요
verified: true 응답만 믿지 마세요. Intel TDX quote를 클라이언트 측에서 파싱하고 측정값이 기대값과 일치하는지 검증하세요. NVIDIA GPU의 경우 NVIDIA 검증 서비스를 통해 attestation을 확인하세요.
매 attestation 요청마다 새 무작위 nonce를 생성하세요. 이로써 공격자가 오래된 attestation을 제공하는 재전송 공격을 방지할 수 있습니다.
signing key는 TDX REPORTDATA 필드에 바인딩되어야 합니다. 이는 키가 enclave 내부에서 생성되었음을 증명합니다.
TDX attestation에 디버그 플래그가 설정되어 있지 않은지 확인하세요. 디버그 enclave는 검사가 가능하므로 프로덕션에서는 신뢰해서는 안 됩니다.
E2EE는 신중한 암호 구현이 필요합니다. 직접 프로토콜을 구현하기보다 공식 SDK를 사용하세요.
모델 기능 확인
다음과 같이 models endpoint를 통해 모델이 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}'
에러 처리
Error Cause Solution TEE attestation verification failedattestation 검증 실패 재시도하거나 지원에 문의 Attestation nonce mismatch재전송 공격 가능성 새 nonce 생성 TDX debug mode detectedenclave가 디버그 모드 프로덕션에서 사용 금지 Failed to decrypt field서버 측 E2EE 복호화 실패 암호화 구현 확인 E2EE requires streamingE2EE 모델에 비스트리밍 요청 stream: true 설정Encrypted field is not valid hexE2EE 헤더와 함께 평문 전송 모든 user/system 메시지를 암호화 Invalid public key잘못된 키 포맷 04로 시작하는 130 hex 문자 사용
문제 해결
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바이트가 아닙니다
모델이 E2EE를 지원하는지 확인 (supportsE2EE: true)
API 키가 유효하고 요청한 모델에 접근 권한이 있는지 확인
Venice API와의 네트워크 연결 확인
헤더로 전송한 공개 키를 생성한 동일한 private key를 사용하고 있는지 확인
응답 콘텐츠가 실제로 hex 인코딩되어 있는지 확인(E2EE 활성)
모델 공개 키가 암호화에 사용된 것과 일치하는지 확인
Encrypted field is not valid hex
E2EE 헤더가 있을 때 모든 user 및 system 역할 메시지는 암호화되어야 합니다
암호화된 콘텐츠가 isValidEncrypted() 검증을 통과하는지 확인(최소 186 hex 문자)
암호화 결과가 prefix 없는 소문자 hex인지 확인
클라이언트 공개 키는 04로 시작하는 정확히 130 hex 문자 여야 합니다
전송 전에 validateClientPubkey() 헬퍼로 포맷을 확인
비압축 공개 키 포맷(65바이트 = 130 hex 문자) 사용 확인
모델 ID가 정확하고 모델이 E2EE를 지원하는지 확인
/models endpoint로 사용 가능한 E2EE 모델을 확인
리소스