Home/Docs/Security

Security Model

Understanding how encryption works and what's protected

1How Encryption Works

Step 1: Key Derivation (TEE)

// Client requests key from TEE
const wrappedKey = await tee.getKey('user.near/private');

// Inside TEE:
// 1. Hardware seed (TEE-protected)
// 2. CKD derive: seed + group_id → AES-256 key
// 3. Wrap key with user's public key
// 4. Return wrapped key + attestation

✓ Key derived in hardware-protected enclave
✓ Nobody can access the hardware seed

Step 2: Key Unwrapping (Client)

// Client unwraps the key
const key = await crypto.subtle.unwrapKey(
  'raw',
  wrappedKey,
  userPrivateKey,  // User's private key
  'RSA-OAEP',
  { name: 'AES-GCM' },
  false,           // Not extractable
  ['encrypt', 'decrypt']
);

✓ Only user can unwrap their keys
⚠ Key stored in browser memory (client-side risk)

Step 3: Encrypt (Client)

async encryptValue(plaintext: string): Promise<string> {
  // 1. Get AES key
  const key = await this.getOrCreateKey();
  
  // 2. Generate random IV (12 bytes)
  const iv = crypto.getRandomValues(new Uint8Array(12));
  
  // 3. Encrypt with AES-256-GCM
  const encrypted = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv, tagLength: 128 },
    key,
    new TextEncoder().encode(plaintext)
  );
  
  // 4. Serialize: enc:AES256:keyId:base64(iv + ciphertext)
  return this.serializeEncrypted(iv, encrypted);
}

✓ AES-256-GCM authenticated encryption
✓ Random IV ensures uniqueness
✓ 128-bit authentication tag

Step 4: Storage

// Store ciphertext only
await this.storage.set('my-secret', encryptedValue);

// Stored value:
// enc:AES256:01560100ddd39635:hM60OnrQ6W2yUKX0QvTCo8iSz2f4g3t...

✓ Only ciphertext stored
✓ No plaintext in storage
✓ No keys in storage

2Threat Model

Protected Against

  • ✓Network observers — ISP, WiFi, proxies only see ciphertext
  • ✓Storage providers — FastKV/blockchain only see ciphertext
  • ✓TEE operator — OutLayer never sees plaintext or keys
  • ✓Other users — Can't access your group's keys
  • ✓Key theft — Keys wrapped with user's public key

NOT Protected Against

  • ✗XSS attacks — Can steal keys from browser memory
  • ✗Malicious extensions — Can intercept plaintext
  • ✗Browser exploits — Memory dumps can expose keys
  • ✗Compromised client — Modified encryption code
  • ✗Key reuse — If key leaked, all data compromised

3Client-Side Encryption Trade-offs

Why Client-Side?

Requests are public — If you sent plaintext to the TEE, anyone observing the network would see it before encryption.

// ❌ If encryption happened in TEE:
POST /encrypt
{
  "plaintext": "my secret"  ← EXPOSED ON NETWORK!
}

// ✅ Client-side encryption:
POST /storage
{
  "value": "enc:AES256:..."  ← ONLY CIPHERTEXT
}
AspectClient-SideTEE-Side
Network exposure✅ Ciphertext only❌ Plaintext exposed
Key security⚠️ Browser memory✅ TEE-protected
XSS protection❌ Can steal keys✅ Can't access TEE
Forward secrecy❌ No✅ Possible
Practicality✅ Works everywhere❌ Violates constraint

4Best Practices

Do

  • ✓ Use Content Security Policy (CSP)
  • ✓ Implement Subresource Integrity (SRI)
  • ✓ Rotate keys regularly
  • ✓ Audit key access logs
  • ✓ Use short-lived sessions
  • ✓ Monitor for suspicious activity
  • ✓ Keep browser/extensions updated
  • ✓ Use HTTPS everywhere

Don't

  • ✗ Store keys in localStorage
  • ✗ Reuse IVs with same key
  • ✗ Share groups between users
  • ✗ Skip attestation verification
  • ✗ Use in insecure contexts (HTTP)
  • ✗ Trust unverified extensions
  • ✗ Ignore security warnings
  • ✗ Log plaintext or keys

5Encryption Parameters

ParameterValueReason
AlgorithmAES-256-GCMAuthenticated encryption
Key Size256 bits (32 bytes)Military-grade security
IV Size96 bits (12 bytes)Optimal for GCM
Tag Length128 bits (16 bytes)Maximum integrity
Key DerivationCKD (HKDF-SHA256)Hardware-backed
WrappingRSA-OAEPSecure key transport

6Summary

TEE Protection

Keys derived in hardware enclave with attestation

Client Encryption

AES-256-GCM in browser to protect data in transit

Trade-offs

Client-side risks exist but are unavoidable given public network