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..."
  ]
}

Verification Process

Step-by-Step Verification

1

Hash Request Content

Compute SHA-256 of your original request:
import hashlib
import json

def hash_request(messages):
    # Normalize request to consistent JSON
    request_data = json.dumps(messages, sort_keys=True)
    return hashlib.sha256(request_data.encode()).hexdigest()
2

Hash Response Content

Compute SHA-256 of received response:
def hash_response(content):
    return hashlib.sha256(content.encode()).hexdigest()
3

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()
4

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
5

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