Skip to main content

What is Signature Verification?

Signature verification provides cryptographic proof that:
  • ✅ A specific request was processed in TEE
  • ✅ The response hasn’t been tampered with
  • ✅ Request and response are cryptographically linked
  • ✅ Execution happened at a specific time
While attestation proves the code runs in TEE, signatures prove that your specific request was processed correctly.

How It Works

Signature Generation

  1. Request enters TEE
  2. TEE processes request and generates response
  3. TEE creates signature over:
    • Request ID
    • Request hash (SHA-256 of your prompt)
    • Response hash (SHA-256 of model output)
    • Timestamp
    • Model used
  4. Signature is created with TEE’s private key
  5. Response returned with signature ID

Signature Retrieval

After receiving a response, fetch its signature:
# 1. Make request, get request ID
RESPONSE=$(curl -s https://api.redpill.ai/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "phala/qwen-2.5-7b-instruct",
    "messages": [{"role": "user", "content": "Hello"}]
  }')

REQUEST_ID=$(echo $RESPONSE | jq -r '.id')

# 2. Get signature
curl "https://api.redpill.ai/v1/signature/$REQUEST_ID?model=phala/qwen-2.5-7b-instruct&signing_algo=ecdsa" \
  -H "Authorization: Bearer YOUR_API_KEY"

Signature API Response

{
  "request_id": "chatcmpl-1029353a55c642248604e36c6a7af6b4",
  "model": "phala/qwen-2.5-7b-instruct",
  "signature": {
    "algorithm": "ecdsa",
    "curve": "secp256k1",
    "value": "3045022100a1b2c3d4e5f6...0201ff",
    "public_key": "04a1b2c3d4e5f6g7h8i9...",
    "message_hash": "7f3b2a1c9d8e7f6a5b4c3d2e1f0a9b8c..."
  },
  "payload": {
    "request_hash": "5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d...",
    "response_hash": "9e8f7d6c5b4a3c2d1e0f9a8b7c6d5e4f...",
    "timestamp": "2025-01-15T10:30:00Z",
    "model": "phala/qwen-2.5-7b-instruct",
    "tee_instance": "nvidia_h100_tee_001"
  },
  "cert_chain": [
    "-----BEGIN CERTIFICATE-----\nMIIC...",
    "-----BEGIN CERTIFICATE-----\nMIID..."
  ]
}

Complete Verification Workflow

To fully verify a signature, you need to:
  1. Verify request/response hashes match
  2. Recover the signing address from the signature
  3. Fetch fresh attestation for that signing address
  4. Verify the complete attestation (proving the signer is genuine TEE)
This creates an end-to-end trust chain from your response → signature → signing key → TEE hardware.

Verification Process

Step-by-Step Verification

1

Get Signature for Response

After making a request, fetch its signature:
import requests

# 1. Make request
response = requests.post(
    "https://api.redpill.ai/v1/chat/completions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "phala/qwen-2.5-7b-instruct",
        "messages": [{"role": "user", "content": "Hello"}]
    }
)
request_id = response.json()["id"]

# 2. Get signature
sig_response = requests.get(
    f"https://api.redpill.ai/v1/signature/{request_id}",
    params={"model": "phala/qwen-2.5-7b-instruct"},
    headers={"Authorization": f"Bearer {API_KEY}"}
)
signature_data = sig_response.json()
2

Verify Request/Response Hashes

Confirm the signature covers your actual request and response:
from hashlib import sha256

def sha256_text(text):
    """Calculate SHA256 hash of text"""
    return sha256(text.encode()).hexdigest()

# Calculate hashes of your original request and response
request_body_json = '{"model": "...", "messages": [...]}'  # Your request JSON
response_body = '{"id": "...", "choices": [...], ...}'     # Full response JSON

request_hash = sha256_text(request_body_json)
response_hash = sha256_text(response_body)

# Parse the signed hashes
hashed_text = signature_data["text"]
request_hash_server, response_hash_server = hashed_text.split(":")

# Verify they match
assert request_hash == request_hash_server, "Request hash mismatch"
assert response_hash == response_hash_server, "Response hash mismatch"
3

Recover Signing Address

Critical step: Recover the address that created this signature.For ECDSA signatures (most common):
from eth_account.messages import encode_defunct
from eth_account import Account

# Recover the address that created this signature
message = encode_defunct(text=signature_data["text"])
recovered_address = Account.recover_message(
    message,
    signature=signature_data["signature"]
)

# Verify it matches the claimed signing address
signing_address = signature_data["signing_address"]
assert recovered_address.lower() == signing_address.lower(), \
    "Signature verification failed"

print(f"Signature valid from: {signing_address}")
For Ed25519 signatures, use appropriate Ed25519 verification libraries.
4

Generate Nonce for Attestation

Generate a fresh random nonce to fetch attestation:
import secrets

# Generate fresh nonce (prevents replay attacks)
nonce = secrets.token_hex(32)
5

Fetch Attestation for Signing Address

Get fresh attestation proving the signing key belongs to verified TEE:
# Fetch attestation for this specific signing address
attestation_response = requests.get(
    "https://api.redpill.ai/v1/attestation/report",
    params={
        "model": "phala/qwen-2.5-7b-instruct",
        "nonce": nonce,
        "signing_address": signing_address
    },
    headers={"Authorization": f"Bearer {API_KEY}"}
)
attestation_report = attestation_response.json()

# Handle multi-server deployments
if "all_attestations" in attestation_report:
    # Filter for matching signing address
    attestation = next(
        item for item in attestation_report["all_attestations"]
        if item["signing_address"].lower() == signing_address.lower()
    )
else:
    attestation = attestation_report

print(f"Found attestation for: {attestation['signing_address']}")
6

Verify Complete Attestation

Most critical step: Verify the full attestation proving this signing key belongs to genuine TEE.See Attestation Guide for complete verification steps:
  1. Verify Intel TDX quote - Proves genuine Intel CPU with TEE
  2. Verify report data binding - Proves signing address and nonce are embedded in hardware attestation
  3. Verify NVIDIA GPU attestation - Proves genuine NVIDIA GPU with nonce
  4. Verify Docker compose manifest - Proves exact software configuration
  5. Verify Sigstore provenance - Proves containers built from expected repos
# Example verification (see attestation guide for full implementation)
def verify_complete_attestation(attestation, nonce, signing_address):
    """Verify all attestation components"""

    # 1. Verify Intel TDX quote
    intel_result = verify_intel_tdx_quote(attestation["intel_quote"])

    # 2. Verify report data binding (CRITICAL)
    report_data = bytes.fromhex(
        intel_result["quote"]["body"]["reportdata"].removeprefix("0x")
    )
    signing_address_bytes = bytes.fromhex(signing_address.removeprefix("0x"))

    assert report_data[:32] == signing_address_bytes.ljust(32, b"\x00"), \
        "Signing address not in TEE report"
    assert report_data[32:64].hex() == nonce, \
        "Nonce mismatch - replay attack"

    # 3. Verify NVIDIA GPU
    verify_nvidia_gpu(attestation, nonce)

    # 4. Verify Docker compose
    verify_docker_compose(attestation, intel_result)

    # 5. Verify Sigstore
    verify_sigstore_provenance(attestation)

    return True
When all checks pass, you’ve proven end-to-end that your response came from verified TEE hardware.
7

Reconstruct Message

Build the message that was signed:
def reconstruct_signed_message(payload):
    message = f"{payload['request_hash']}|{payload['response_hash']}|{payload['timestamp']}|{payload['model']}"
    return hashlib.sha256(message.encode()).hexdigest()
8

Verify Signature

Verify ECDSA signature:
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes

def verify_signature(signature_value, message_hash, public_key):
    public_key.verify(
        bytes.fromhex(signature_value),
        bytes.fromhex(message_hash),
        ec.ECDSA(hashes.SHA256())
    )
    return True
9

Verify Certificate Chain

Ensure public key certificate chains to TEE root:
def verify_cert_chain(cert_chain):
    # Verify each certificate against its issuer
    # Ensure final cert is TEE root certificate
    return True

Complete Code Example

import requests
import hashlib
import json
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend

class SignatureVerifier:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.redpill.ai/v1"

    def make_request(self, model, messages):
        """Make API request and return response with ID"""
        response = requests.post(
            f"{self.base_url}/chat/completions",
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json"
            },
            json={
                "model": model,
                "messages": messages
            }
        )
        return response.json()

    def get_signature(self, request_id, model):
        """Fetch signature for a request"""
        response = requests.get(
            f"{self.base_url}/signature/{request_id}",
            params={"model": model, "signing_algo": "ecdsa"},
            headers={"Authorization": f"Bearer {self.api_key}"}
        )
        return response.json()

    def hash_content(self, content):
        """Hash content with SHA-256"""
        if isinstance(content, dict) or isinstance(content, list):
            content = json.dumps(content, sort_keys=True)
        return hashlib.sha256(content.encode()).hexdigest()

    def verify_signature(self, original_request, original_response, signature_data):
        """Verify complete signature"""
        # 1. Hash original request and response
        request_hash = self.hash_content(original_request["messages"])
        response_hash = self.hash_content(original_response["choices"][0]["message"]["content"])

        # 2. Verify hashes match signature payload
        if request_hash != signature_data["payload"]["request_hash"]:
            raise ValueError("Request hash mismatch")
        if response_hash != signature_data["payload"]["response_hash"]:
            raise ValueError("Response hash mismatch")

        # 3. Reconstruct signed message
        message = (
            f"{signature_data['payload']['request_hash']}|"
            f"{signature_data['payload']['response_hash']}|"
            f"{signature_data['payload']['timestamp']}|"
            f"{signature_data['payload']['model']}"
        )
        message_hash = hashlib.sha256(message.encode()).digest()

        # 4. Load public key
        public_key_bytes = bytes.fromhex(signature_data["signature"]["public_key"])
        public_key = ec.EllipticCurvePublicKey.from_encoded_point(
            ec.SECP256K1(),
            public_key_bytes
        )

        # 5. Verify ECDSA signature
        signature_bytes = bytes.fromhex(signature_data["signature"]["value"])
        try:
            public_key.verify(
                signature_bytes,
                message_hash,
                ec.ECDSA(hashes.SHA256())
            )
            return True
        except Exception as e:
            raise ValueError(f"Signature verification failed: {e}")

# Usage Example
verifier = SignatureVerifier("YOUR_API_KEY")

# 1. Make request
request_data = {
    "model": "phala/qwen-2.5-7b-instruct",
    "messages": [{"role": "user", "content": "What is 2+2?"}]
}
response = verifier.make_request(
    request_data["model"],
    request_data["messages"]
)

print(f"Response: {response['choices'][0]['message']['content']}")

# 2. Get signature
signature = verifier.get_signature(
    response["id"],
    request_data["model"]
)

# 3. Verify signature
try:
    verifier.verify_signature(request_data, response, signature)
    print("✅ Signature verified! Response is authentic.")
except ValueError as e:
    print(f"❌ Verification failed: {e}")

Signature Algorithms

RedPill supports multiple signature algorithms:
AlgorithmCurve/Key SizeSecurity LevelUse Case
ecdsasecp256k1128-bitDefault, Bitcoin-compatible
ecdsa-p256NIST P-256128-bitStandards-compliant
rsaRSA-2048112-bitLegacy compatibility
Specify algorithm in request:
curl "https://api.redpill.ai/v1/signature/$REQUEST_ID?model=phala/qwen-2.5-7b-instruct&signing_algo=ecdsa-p256" \
  -H "Authorization: Bearer YOUR_API_KEY"

Use Cases

1. Compliance Auditing

Keep signed records for regulatory compliance:
def create_audit_record(request, response, signature):
    """Create tamper-proof audit record"""
    audit_entry = {
        "timestamp": signature["payload"]["timestamp"],
        "request_id": signature["request_id"],
        "model": signature["model"],
        "request_hash": signature["payload"]["request_hash"],
        "response_hash": signature["payload"]["response_hash"],
        "signature": signature["signature"]["value"],
        "verified": True
    }
    # Store in immutable audit log
    save_to_blockchain(audit_entry)

2. Non-Repudiation

Prove a specific response came from RedPill TEE:
def prove_response_authenticity(request_id, model):
    """Get verifiable proof of response"""
    signature = get_signature(request_id, model)

    # Signature proves:
    # 1. Request was processed in TEE
    # 2. Response hasn't been modified
    # 3. Execution time is accurate
    # 4. TEE instance is genuine

    return signature

3. Response Integrity

Detect tampering with responses:
def verify_response_integrity(original_response, signature):
    """Ensure response hasn't been modified"""
    current_hash = hash_content(original_response)
    expected_hash = signature["payload"]["response_hash"]

    if current_hash != expected_hash:
        raise ValueError("Response has been tampered with!")

    return True

4. Dispute Resolution

Resolve disputes with cryptographic proof:
# User claims: "I never asked for this"
# You prove: "Here's the signed request hash"

def prove_request_was_made(request_id, model):
    signature = get_signature(request_id, model)

    # Signature contains:
    # - request_hash (proves what was asked)
    # - timestamp (proves when)
    # - TEE signature (proves authenticity)

    return signature

Best Practices

For high-value or sensitive requests, always verify signatures:
if request_value > 1000:  # High value
    verify_signature(request, response, signature)
Keep signatures for compliance:
# Store for 7 years (typical regulatory requirement)
store_signature_with_retention(signature, years=7)
Reject old signatures to prevent replay:
from datetime import datetime, timedelta

sig_time = datetime.fromisoformat(signature["payload"]["timestamp"])
if datetime.utcnow() - sig_time > timedelta(hours=1):
    raise ValueError("Signature too old")
Always verify public key chains to TEE root:
verify_cert_chain(signature["cert_chain"])
Prevent duplicate processing:
headers = {
    "Idempotency-Key": "unique-request-id-123"
}

Troubleshooting

Cause: Signatures are generated asynchronouslySolution:
  • Wait 1-2 seconds after request
  • Retry signature retrieval
  • Check request ID is correct
Cause: Request content was normalized differentlySolution:
  • Ensure consistent JSON serialization (sort keys)
  • Remove whitespace
  • Use same encoding (UTF-8)
Cause: Incorrect public key format or signature parsingSolution:
  • Verify you’re using correct curve (secp256k1)
  • Check signature is properly hex-decoded
  • Ensure public key is uncompressed format
Cause: Missing root certificates or expired intermediatesSolution:
  • Update root certificate bundle
  • Check certificate expiration dates
  • Verify internet access to CRL endpoints

Signature vs Attestation

FeatureSignatureAttestation
ScopeSingle request/responseEntire TEE instance
GranularityPer-requestPer-model or gateway
ProvesThis response is authenticCode runs in TEE
FrequencyEvery requestPeriodic (hourly/daily)
Use CaseNon-repudiation, auditTrust establishment
PerformanceFast (~1ms)Slower (~100ms)
Best Practice: Use attestation to verify TEE, then use signatures for individual request verification.

FAQs

Yes, but signatures are most useful for Phala models where both gateway and inference are TEE-protected.
Signatures are available for 90 days. Download and store if needed longer.
Yes! Once you have the signature and TEE root certificates, verification is fully offline.
Either the response was tampered with, or there’s a verification implementation error. Check your code first, then contact support.
Signature generation adds ~1ms. Retrieval is a separate API call you control.

Next Steps