Documentation Index Fetch the complete documentation index at: https://docs.shieldhq.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Encryption Flow
This document details the cryptographic operations that secure your content in SHIELD.
Overview
SHIELD uses a symmetric encryption model where the same key encrypts and decrypts content. The key itself is never transmitted to any server—it’s embedded in the URL fragment and stays in the browser.
Encryption Algorithm
AES-GCM-256
We use AES-GCM (Galois/Counter Mode) with a 256-bit key:
Confidentiality : AES encryption provides strong data protection
Authentication : GCM mode includes built-in authentication (no separate MAC needed)
Performance : Hardware-accelerated in modern browsers
Standard : NIST-approved, widely audited
┌─────────────────────────────────────────────────────┐
│ AES-GCM ENCRYPTION │
├─────────────────────────────────────────────────────┤
│ Plaintext │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌──────────┐ ┌──────┐ │
│ │ Counter Mode │◀───│ IV │ │ │ │
│ │ (Encryption) │ │ (96-bit)│ │ │ │
│ └────────┬────────┘ └──────────┘ │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌─────────────────┐ │ │ │
│ │ Galois Field │◀──────────────────│ Key │ │
│ │ Multiplication │ │(256b)│ │
│ │ (Auth Tag) │ │ │ │
│ └────────┬────────┘ │ │ │
│ │ │ │ │
│ ┌─────┴──────┐ └──────┘ │
│ ▼ ▼ │
│ Ciphertext Auth Tag (128-bit) │
└─────────────────────────────────────────────────────┘
Key Generation
Random Key Generation
const generateKey = async () => {
return await crypto . subtle . generateKey (
{
name: 'AES-GCM' ,
length: 256
},
true , // extractable
[ 'encrypt' , 'decrypt' ]
);
};
The key is generated using the browser’s cryptographically secure random number generator (CSPRNG).
IV Generation
const generateIV = () => {
return crypto . getRandomValues ( new Uint8Array ( 12 )); // 96 bits
};
Each encryption uses a unique IV, never reused with the same key.
Encryption Process
Step-by-Step
async function encryptContent ( fileData ) {
// 1. Generate random key
const key = await crypto . subtle . generateKey (
{ name: 'AES-GCM' , length: 256 },
true ,
[ 'encrypt' , 'decrypt' ]
);
// 2. Generate random IV
const iv = crypto . getRandomValues ( new Uint8Array ( 12 ));
// 3. Encrypt
const ciphertext = await crypto . subtle . encrypt (
{ name: 'AES-GCM' , iv },
key ,
fileData
);
// 4. Combine IV + Ciphertext + AuthTag
const encrypted = new Uint8Array ( iv . length + ciphertext . byteLength );
encrypted . set ( iv );
encrypted . set ( new Uint8Array ( ciphertext ), iv . length );
// 5. Export key for URL
const keyBuffer = await crypto . subtle . exportKey ( 'raw' , key );
const secretKey = arrayBufferToBase64Url ( keyBuffer );
return { encrypted , secretKey };
}
The encrypted payload sent to IPFS:
Bytes Content Size 0-11 IV 12 bytes 12-(n-16) Ciphertext variable (n-15)-n Auth Tag 16 bytes
Decryption Process
async function decryptContent ( encryptedData , secretKey ) {
// 1. Import key from URL
const keyBuffer = base64UrlToArrayBuffer ( secretKey );
const key = await crypto . subtle . importKey (
'raw' ,
keyBuffer ,
{ name: 'AES-GCM' },
false ,
[ 'decrypt' ]
);
// 2. Extract IV and ciphertext
const iv = encryptedData . slice ( 0 , 12 );
const ciphertext = encryptedData . slice ( 12 );
// 3. Decrypt
const plaintext = await crypto . subtle . decrypt (
{ name: 'AES-GCM' , iv },
key ,
ciphertext
);
return plaintext ;
}
URL Fragment Encoding
The secret key is encoded for the URL fragment:
https://app.shieldhq.xyz/r/{policyId}#{secretKey}
│
└── base64url encoded
Why URL Fragment?
Never Sent to Server Browsers don’t include the fragment in HTTP requests. The key stays client-side.
No Server Access Even SHIELD’s servers cannot see or log the decryption key.
Base64url Encoding
// Convert ArrayBuffer to base64url string
function arrayBufferToBase64Url ( buffer ) {
const base64 = btoa ( String . fromCharCode ( ... new Uint8Array ( buffer )));
return base64 . replace ( / \+ / g , '-' ). replace ( / \/ / g , '_' ). replace ( /=/ g , '' );
}
// Convert base64url back to ArrayBuffer
function base64UrlToArrayBuffer ( str ) {
const base64 = str . replace ( /-/ g , '+' ). replace ( /_/ g , '/' );
const padded = base64 . padEnd ( base64 . length + ( 4 - base64 . length % 4 ) % 4 , '=' );
const binary = atob ( padded );
return Uint8Array . from ( binary , c => c . charCodeAt ( 0 ));
}
Security Properties
Confidentiality
256-bit keys : Brute-force infeasible (2^256 possibilities)
Unique IVs : Same content encrypts differently each time
Authenticated encryption : Tampering is detected
Authentication
GCM tag : 128-bit authentication tag prevents tampering
Contract verification : Policy must be valid before decryption
Forward Secrecy
Per-content keys : Each link has a unique key
No key storage : Keys only exist in shared links
Irrecoverable : Lost links cannot be regenerated
Threat Model
Threat Mitigation Server compromise Server never sees keys or plaintext Man-in-the-middle HTTPS + contract verification Link interception Recipients authenticate with wallet Brute force 256-bit keys, rate limiting Replay attacks On-chain attempt tracking Tampered content GCM authentication tag