Documentazione Tecnica
& Architettura di Sicurezza

Sistema Enterprise di Cloud Storage con Crittografia End-to-End
e Architettura Zero-Knowledge Certificata

AES-256-GCM
RSA-4096
Zero-Knowledge
Enterprise-Grade

Panoramica della Sicurezza

ZeroKeep implementa un'architettura di sicurezza multi-livello basata su standard crittografici militari e best practices dell'industria. Il sistema è progettato per garantire la massima privacy degli utenti attraverso crittografia end-to-end e un modello zero-knowledge che impedisce qualsiasi accesso non autorizzato ai dati, incluso lo stesso fornitore del servizio.

256-bit
AES-GCM Encryption
4096-bit
RSA-OAEP Keys
100k+
PBKDF2 Iterations
0%
Server Knowledge

Crittografia Client-Side

Tutti i file vengono crittografati nel browser dell'utente prima della trasmissione. Il server riceve solo ciphertext incomprensibile.

Certificato

Zero-Knowledge Server

Il server non ha mai accesso alle chiavi di decifratura. Anche in caso di compromissione, i dati rimangono protetti.

Verificato

Authenticated Encryption

AES-256-GCM garantisce sia confidenzialità che autenticità dei dati, prevenendo manipolazioni.

AEAD Mode

Forward Secrecy

Ogni file utilizza una chiave unica generata casualmente. Compromettere una chiave non espone altri file.

Per-File Keys

Audit Trail Completo

Ogni operazione viene registrata con timestamp crittografico per garantire tracciabilità e non-ripudio.

Compliance Ready

Password Security

PBKDF2 con 100.000 iterazioni e bcrypt server-side garantiscono resistenza a attacchi brute-force.

OWASP Compliant
Garanzia di Sicurezza

ZeroKeep utilizza solo algoritmi crittografici standardizzati e verificati dalla comunità crittografica internazionale. Nessun algoritmo proprietario o offuscato viene utilizzato. Il codice sorgente client è ispezionabile per verificare l'implementazione corretta degli standard di sicurezza.

Architettura Zero-Knowledge

Il concetto di "Zero-Knowledge" significa che il server non ha mai accesso ai dati in chiaro né alle chiavi di decifratura. Questa architettura garantisce che anche in caso di compromissione completa del server, i dati degli utenti rimangono completamente protetti e inaccessibili.

Principi Fondamentali

Crittografia Lato Client

Principio: Tutta la crittografia avviene nel browser dell'utente utilizzando Web Crypto API nativa.

Implementazione:

  • Generazione chiavi casuali (CSPRNG)
  • Cifratura AES-256-GCM prima dell'upload
  • Decifratura solo dopo il download
  • Chiavi mai trasmesse al server

Derivazione Chiavi Sicura

Principio: Le chiavi master sono derivate dalle password utente utilizzando funzioni crittografiche robuste.

Implementazione:

  • PBKDF2 con 100.000 iterazioni
  • Salt unico per utente (256-bit)
  • SHA-256 come funzione hash
  • Argon2id sul server per ulteriore protezione

Metadata Crittografati

Principio: Anche i metadati dei file (nome, dimensione, tipo) sono crittografati.

Implementazione:

  • Metadata serializzati in JSON
  • Crittografati con chiave master utente
  • Server vede solo hash e dimensione ciphertext
  • Nomi file obfuscati con hash

Separazione Chiavi

Principio: Ogni tipo di dato utilizza chiavi diverse e indipendenti.

Implementazione:

  • DEK (Data Encryption Key) per file
  • KEK (Key Encryption Key) per wrapping
  • Master Key per metadata
  • RSA Keys per condivisione E2EE
Cosa Significa per la Privacy

Il server ZeroKeep non può:

  • Vedere il contenuto dei file
  • Leggere i nomi dei file
  • Conoscere il tipo di file caricati
  • Accedere alle password degli utenti
  • Decifrare le comunicazioni E2EE
Flusso Architettura Zero-Knowledge
CLIENT SIDE (Browser) Utente Password + File Originale PBKDF2 Key Derivation 100,000 iterations + Salt (256-bit) → Master Key (256-bit) Random DEK Generation crypto.getRandomValues(256-bit) → Unique DEK per file AES-256-GCM Encryption File + DEK + IV (96-bit) → Ciphertext + Auth Tag Key Wrapping (AES-KW) DEK + Master Key → Wrapped DEK HTTPS Upload (Solo dati cifrati) SERVER SIDE Storage S3 COSA VEDE IL SERVER: ✗ Contenuto file (cifrato) ✗ Nome file (hash) ✗ Wrapped DEK (cifrata) ✗ Metadata (cifrati) ✓ Solo dimensione totale Database PostgreSQL • file_hash (SHA-256) • wrapped_dek_encrypted • metadata_encrypted • timestamp, size_bytes ZERO-KNOWLEDGE GUARANTEE Il server NON può: ✗ Decifrare i file ✗ Vedere le chiavi DEK ✗ Accedere alla Master Key ✗ Leggere metadata in chiaro
Importante: Responsabilità dell'Utente

A causa dell'architettura zero-knowledge, se un utente perde la propria password, non esiste modo di recuperare i file. ZeroKeep non può reimpostare la password o fornire accesso alternativo. È fondamentale che gli utenti conservino le proprie password in modo sicuro.

Standard Crittografici Implementati

ZeroKeep utilizza esclusivamente algoritmi crittografici standardizzati da NIST e raccomandati da ENISA, implementati attraverso le API native del browser (Web Crypto API) per garantire sicurezza e prestazioni ottimali.

Algoritmi e Parametri

AES-256-GCM

Cifratura Simmetrica AEAD

Utilizzo: Crittografia file e dati sensibili

Algorithm: AES-GCM
Key Length: 256-bit (32 bytes)
IV Length: 96-bit (12 bytes)
Tag Length: 128-bit (16 bytes)
Mode: Galois/Counter Mode (AEAD)

// Web Crypto API Implementation
const key = await crypto.subtle.generateKey(
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
);

const iv = crypto.getRandomValues(new Uint8Array(12));

const ciphertext = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv: iv },
    key,
    plaintext
);

Proprietà:

  • Confidenzialità (encryption)
  • Autenticità (authentication tag)
  • Integrità (tamper detection)
  • Prestazioni elevate (hardware-accelerated)
NIST FIPS 197

RSA-4096-OAEP

Crittografia Asimmetrica

Utilizzo: Condivisione E2EE tra utenti

Algorithm: RSA-OAEP
Modulus Length: 4096-bit
Hash Function: SHA-256
MGF: MGF1 with SHA-256
Public Exponent: 65537 (0x10001)

// Key Generation
const keyPair = await crypto.subtle.generateKey(
    {
        name: "RSA-OAEP",
        modulusLength: 4096,
        publicExponent: new Uint8Array([1, 0, 1]),
        hash: "SHA-256"
    },
    true,
    ["encrypt", "decrypt"]
);

// Encrypt DEK for recipient
const encryptedDEK = await crypto.subtle.encrypt(
    { name: "RSA-OAEP" },
    recipientPublicKey,
    dek
);

Proprietà:

  • Scambio chiavi sicuro
  • Resistente a quantum (fino al 2030)
  • Non-ripudio crittografico
  • Protezione padding (OAEP)
PKCS#1 v2.2

PBKDF2-SHA256

Derivazione Chiavi Password

Utilizzo: Derivazione master key da password

Algorithm: PBKDF2
Hash Function: SHA-256
Iterations: 100,000
Salt Length: 256-bit (32 bytes)
Output Length: 256-bit (32 bytes)

// Key Derivation
const masterKey = await crypto.subtle.deriveKey(
    {
        name: "PBKDF2",
        salt: salt,
        iterations: 100000,
        hash: "SHA-256"
    },
    passwordKey,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
);

Proprietà:

  • Resistenza a brute-force
  • Resistenza a rainbow tables
  • Slow by design (computazionalmente costoso)
  • Salt unico per utente
NIST SP 800-132

AES-KW (Key Wrap)

Wrapping Chiavi Simmetriche

Utilizzo: Protezione e storage sicuro delle DEK

Algorithm: AES-KW
Key Length: 256-bit
Standard: RFC 3394

// Wrap DEK with Master Key
const wrappedDEK = await crypto.subtle.wrapKey(
    "raw",
    dekKey,
    masterKey,
    { name: "AES-KW" }
);

// Unwrap DEK
const unwrappedDEK = await crypto.subtle.unwrapKey(
    "raw",
    wrappedDEK,
    masterKey,
    { name: "AES-KW" },
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
);

Proprietà:

  • Protezione chiavi simmetriche
  • Integrità chiave wrappata
  • Dimensione output compatta
  • Verifica autenticità al unwrap
RFC 3394
Conformità agli Standard

Tutti gli algoritmi crittografici implementati sono conformi agli standard internazionali:

  • NIST (National Institute of Standards and Technology)
  • FIPS 140-2/140-3 (Federal Information Processing Standards)
  • ENISA (European Union Agency for Cybersecurity)
  • RFC IETF (Internet Engineering Task Force)

Modello di Minaccia e Difese

ZeroKeep è progettato per resistere a molteplici vettori di attacco. Di seguito l'analisi completa delle minacce considerate e delle relative contromisure implementate.

Vettori di Attacco Analizzati

Compromissione Server

RISCHIO: ALTO | IMPATTO: ZERO

Scenario: Attaccante ottiene accesso completo al server (root access, database, storage S3)

Cosa può ottenere:

  • File cifrati (inutilizzabili senza chiavi)
  • DEK wrappate (protette da master key utente)
  • Metadata cifrati (illeggibili)
  • Hash password (bcrypt con salt)
  • NON può decifrare nulla

Difese:

Zero-Knowledge Architecture
Client-Side Encryption

Man-in-the-Middle (MITM)

RISCHIO: MEDIO | IMPATTO: BASSO

Scenario: Attaccante intercetta comunicazioni tra client e server

Cosa può ottenere:

  • Ciphertext in transito (inutilizzabile)
  • Wrapped DEK (ancora cifrata)
  • NON può vedere file in chiaro
  • NON può ottenere password

Difese:

TLS 1.3 Encryption
Certificate Pinning Ready

Brute-Force Password

RISCHIO: BASSO | IMPATTO: UTENTE-SPECIFICO

Scenario: Attaccante tenta di indovinare password utente

Difese Multi-Livello:

  • Client: PBKDF2 100k iterations (rallenta attacchi offline)
  • Server: bcrypt (rallenta attacchi online)
  • Network: Rate limiting (5 tentativi/minuto)
  • Account: Lockout temporaneo dopo N tentativi

Tempo stimato per crack:

Password 12 caratteri (lettere+numeri+simboli):
- Entropia: ~75 bit
- PBKDF2 100k: ~10ms per tentativo
- Tempo crack: > 2^75 * 10ms = ~1.2 * 10^15 anni

Con GPU cluster (1 trilione hash/sec):
- Ancora > 10^7 anni per password forte

Phishing & Social Engineering

RISCHIO: MEDIO | IMPATTO: UTENTE-SPECIFICO

Scenario: Attaccante cerca di ottenere credenziali utente via phishing

Difese:

  • Domain verification chiara nell'UI
  • 2FA opzionale (TOTP)
  • Email alerts per login da nuovo dispositivo
  • Session management con device fingerprinting
  • User education e security awareness

Responsabilità Utente: Anche con architettura zero-knowledge, gli utenti devono proteggere le proprie password. Raccomandiamo l'uso di password manager e autenticazione a due fattori.

Vulnerabilità Implementazione

RISCHIO: BASSO | IMPATTO: VARIABILE

Scenario: Bug nel codice client/server espone vulnerabilità

Difese:

  • Input Validation: Sanitizzazione completa lato server
  • SQL Injection: Prepared statements (Go database/sql)
  • XSS: Content Security Policy + output escaping
  • CSRF: Token anti-CSRF per tutte le operazioni sensibili
  • Path Traversal: Validazione strict dei percorsi file
  • Code Audits: Review regolari del codice
  • Dependency Updates: Monitoring vulnerabilità CVE

Quantum Computing

RISCHIO: FUTURO | IMPATTO: ALTO (post-2030)

Scenario: Computer quantistici rendono RSA-4096 vulnerabile

Status Attuale:

  • RSA-4096 sicuro fino a ~2030-2035
  • AES-256 resistente anche a quantum (Grover's algorithm richiede 2^128 operazioni)
  • SHA-256 resistente a quantum

Piano Mitigazione:

  • Monitoring sviluppi NIST post-quantum cryptography
  • Migrazione futura a algoritmi quantum-resistant (es. CRYSTALS-Kyber)
  • Architettura modulare permette aggiornamento algoritmi
Defense in Depth

ZeroKeep implementa una strategia di difesa in profondità (defense in depth) con multipli livelli di sicurezza. Anche se un singolo meccanismo venisse compromesso, gli altri livelli continuano a proteggere i dati degli utenti.

Flusso 1: Registrazione Utente

Il processo di registrazione inizializza l'architettura zero-knowledge generando tutte le chiavi crittografiche necessarie sul client.

Diagramma Completo Registrazione
CLIENT BROWSER 1. Utente inserisce credenziali Email + Password (min 12 caratteri) 2. Generazione Salt + Hash Password salt = crypto.getRandomValues(32 bytes) hash = SHA-256(password) 3. Derivazione Master Key (PBKDF2) iterations: 100,000 | hash: SHA-256 → Master Key (256-bit) 4. Generazione Chiavi RSA-4096 RSA-OAEP | modulusLength: 4096 → Public Key + Private Key 5. Cifratura Private Key AES-256-GCM(privateKey, masterKey) → Encrypted Private Key 6. Storage Locale (IndexedDB) Encrypted Private Key → IndexedDB 7. Invio al Server POST /api/v1/auth/register HTTPS Request username_enc password_hash salt public_key_pem SERVER 1. Validazione Input Controlla formato email, lunghezza password 2. Bcrypt Server-Side final_hash = bcrypt(password_hash, cost=12) → Double Hashing Protection 3. Creazione Record Database INSERT INTO users (...) INSERT INTO user_keys (public_key) 4. Generazione Tenant ID tenant_id = UUID-v4 Isolamento storage per utente 5. Generazione Session Token token = crypto.randomBytes(32) expires: 7 giorni (sliding window) 6. Risposta Success 201 Created { user_id, tenant_id, token } ✓ REGISTRAZIONE COMPLETATA Utente pronto per upload/condivisione
Nota sulla Derivazione Chiavi

La password non viene mai inviata al server. Solo l'hash SHA-256 viene trasmesso, e il server applica un ulteriore bcrypt. Questo sistema di double hashing offre protezione anche se il database venisse compromesso.

Flusso 2: Upload File Sicuro

Ogni file viene crittografato con una chiave unica (DEK) generata casualmente, garantendo forward secrecy e isolamento tra file.

Diagramma Upload E2EE Completo
CLIENT (Browser) SERVER 1. Utente seleziona file File: documento.pdf (2.5 MB) 2. Generazione DEK Casuale dek = crypto.subtle.generateKey(AES-GCM, 256-bit) 3. Generazione IV (Nonce) iv = crypto.getRandomValues(12 bytes) 4. Cifratura File AES-256-GCM ciphertext = AES-GCM.encrypt(file, dek, iv) → Ciphertext + Auth Tag 5. Wrapping DEK con Master Key wrapped_dek = AES-KW.wrap(dek, master_key) → DEK protetta da password 6. Cifratura Metadata metadata = {name, size, type, date} encrypted_meta = AES-GCM(metadata, master_key) 7. Upload HTTPS POST /api/v1/files/upload (multipart/form-data) TLS 1.3 Upload encrypted_file wrapped_dek encrypted_meta iv, tag file_hash 1. Receive Upload Request Validate JWT, check quota, verify file hash 2. Store Encrypted File su Cubbit S3 s3_path = s3://bucket/tenant_id/file_hash PUT encrypted_file → S3 3. Save Metadata in Database INSERT INTO files ( file_hash, wrapped_dek, encrypted_metadata, s3_path, iv, tag, size, tenant_id ) 4. Response Success 201 Created { file_id, file_hash, uploaded_at } ✓ UPLOAD COMPLETATO File crittografato E2EE e archiviato SERVER NON PUÒ: ✗ Decifrare il file (no DEK) ✗ Vedere il nome originale (metadata cifrati) ✗ Accedere alla Master Key (solo client) ✓ Solo storage blind di blob cifrato

Cosa il Server NON Può Fare

Vedere Contenuto File

Il server riceve solo il ciphertext cifrato con AES-256-GCM. Senza la DEK (che è wrappata con la master key dell'utente), il file è completamente illeggibile.

Conoscere Nome File

I metadata (nome, tipo, dimensione) sono crittografati con la master key. Il server vede solo un blob cifrato e uno hash.

Ottenere la DEK

La DEK è wrappata con AES-KW usando la master key dell'utente, derivata dalla password. Impossibile unwrappare senza password.

Correlare File tra Utenti

Ogni utente ha tenant isolato, nomi file hashati, DEK uniche. Impossibile correlare file anche se identici.

Flow 3: Download & Decryption

Zero-Knowledge Download

Il server invia solo dati cifrati. La decryption avviene interamente nel browser con le chiavi locali dell'utente.

Diagramma Download E2EE Completo
CLIENT (Browser) SERVER (Backend) 1. User Request Download GET /api/files/:fileId/download HTTPS Request 2. Validate User Access • Check JWT token • Verify file ownership 3. Fetch Encrypted Metadata • encrypted_dek_with_kek (from DB) • s3_path, iv, tag 4. Download from Cubbit S3 GET s3://bucket/encrypted_file Encrypted Data 5. Receive Encrypted Package • encrypted_file (blob) • encrypted_dek_with_kek, iv, tag 6. Derive KEK from Password KEK = PBKDF2(password, salt, 100k iterations) 7. Unwrap DEK DEK = AES-KW.unwrap(KEK, encrypted_dek) 8. Decrypt File with AES-256-GCM plaintext = AES-GCM.decrypt(DEK, iv, encrypted_file, tag) → File decrypted and ready 9. Trigger Browser Download / Display File pronto per l'utente ✓ DOWNLOAD COMPLETATO Zero-Knowledge Download Sicuro

Server Non Vede il File

Il server trasmette solo blob cifrato. Impossibile leggere contenuto senza password utente.

Derivazione KEK Client-Side

PBKDF2 eseguito nel browser. La password non lascia mai il client.

Verifica Integrità con GCM

Il tag GCM garantisce che il file non sia stato alterato durante storage o trasmissione.

Flow 4: Condivisione Pubblica con Password

Crittografia Aggiuntiva per Link Pubblici

Il file già cifrato con DEK viene ri-cifrato con una password condivisa, permettendo accesso anche a non-utenti.

Diagramma Condivisione Pubblica con Password
OWNER (Client) SERVER 1. Genera Password Condivisione share_password = random(16 chars) 💡 Copiata da utente 2. Deriva Share-KEK share_kek = PBKDF2(share_password, salt, 100k) 3. Ri-cifra DEK con Share-KEK encrypted_dek_share = AES-KW.wrap(share_kek, DEK) → DEK protetta da share_password 4. Invio al Server POST /api/shares (encrypted_dek_share, salt, ...) HTTPS 1. Crea Share Record share_id = UUID-v4 INSERT INTO shares (share_id, encrypted_dek_share, salt, expiration, max_downloads, file_hash) 2. Return Share URL 201 Created https://onionedcloud.onion/share/{share_id} 3. Memorizza Configurazione • Link valido per X giorni • Max Y downloads consentiti RECIPIENT SIDE RECIPIENT (Utente Esterno) 1. Accede al Link di Condivisione GET /share/{share_id} → Pagina inserimento password 2. Inserisce Share Password Password ricevuta da owner via canale separato Server deriva KEK e verifica (senza vedere password in chiaro) 3. Download File Cifrato Server invia: encrypted_file + encrypted_dek_share + iv + tag Tutto ancora cifrato! 4. Decrypt Locale nel Browser 1. share_kek = PBKDF2(share_password) → 2. dek = AES-KW.unwrap(share_kek) 3. plaintext = AES-GCM.decrypt(dek, encrypted_file) ✓ FILE CONDIVISO IN MODO SICURO - ZERO KNOWLEDGE

Zero-Knowledge anche per Share

Server non conosce share_password. Verifica tramite KEK derivation ma non può decryptare.

Expiration & Limits

Link scade dopo X giorni o Y download. Mitigazione furto link.

Out-of-Band Password

Best practice: inviare link e password via canali separati (email + SMS).

Flow 5: Condivisione E2EE tra Utenti

Massima Sicurezza con RSA-4096

Il DEK è cifrato con la chiave pubblica RSA del destinatario. Solo lui può decryptare con la sua chiave privata.

Diagramma Condivisione E2EE tra Utenti
SENDER SERVER RECEIVER 1. Request Public Key GET /api/users/{email}/public-key 2. Fetch & Return Public Key RSA-4096 public key (PEM format) 3. Encrypt DEK with RSA encrypted_dek_rsa = RSA-OAEP.encrypt(recipient_pub, DEK) 4. Create E2EE Share POST /api/shares/e2ee { fileId, recipientEmail, encrypted_dek_rsa } 5. Store E2EE Share • share_id (UUID) • sender_id, recipient_id • encrypted_dek_rsa (blob) 6. Notifica Recipient (optional) Notification 7. List Shared Files GET /api/shares/received 8. Download File Data encrypted_dek_rsa, file blob 9. Decrypt DEK DEK = RSA-OAEP.decrypt( private_key, encrypted_dek_rsa) 10. Decrypt File file = AES-GCM.decrypt(DEK, blob) File Accessible Server CANNOT decrypt No access to recipient private key

RSA-4096 Asymmetric

Solo il recipient può decryptare con la sua chiave privata, che non lascia mai il browser.

Server Zero-Knowledge

Server memorizza encrypted_dek_rsa ma non può leggerlo. Non ha accesso alla chiave privata.

Forward Secrecy

Compromissione server futuro non espone file passati. Ogni DEK è unica e cifrata per recipient.

Revoca Condivisione

Owner può revocare share eliminando il record DB. Recipient perde accesso immediato.

Flow 6: RSA Key Generation & Exchange

Generazione Coppia Chiavi durante Registrazione

Ogni utente genera una coppia RSA-4096 durante la registrazione. La chiave privata è cifrata con KEK e mai trasmessa in chiaro.

Diagramma RSA Key Generation & Exchange
CLIENT (Browser) SERVER 1. User Completa Registrazione Form 2. Deriva KEK da Password KEK = PBKDF2-SHA256(password, salt, 100k) 3. Genera Coppia RSA-4096 crypto.subtle.generateKey(RSA-OAEP, 4096) → { publicKey, privateKey } 4. Cifra Chiave Privata con KEK encrypted_private_key = AES-GCM.encrypt(KEK, privateKey) → Private key protetta da password 5. Invio al Server POST /api/register (publicKey, encrypted_private_key, iv) HTTPS 6. Salva Chiavi in Database users.rsa_public_key = publicKey (PEM) users.rsa_encrypted_private_key = encrypted_private_key users.rsa_private_key_iv = iv ✓ Public: leggibile | Private: cifrata Server non può decifrare private key! 🔒 ZERO-KNOWLEDGE Server NON può decifrare senza password utente LOGIN & KEY RECOVERY CLIENT (Browser) SERVER 7. User Login → Deriva KEK KEK = PBKDF2-SHA256(password, salt, 100k) 8. Richiedi Chiave Privata Cifrata GET /api/keys/private HTTPS GET 8. Return Chiave Privata Cifrata Response: { encrypted_private_key, iv } Server invia chiave ANCORA CIFRATA 9. Decrypt Private Key con KEK privateKey = AES-GCM.decrypt(KEK, encrypted_private_key, iv) → Store in Memory (Session) → Ready for E2EE Sharing! ⚠️ Private key MAI salvata in chiaro sul server ✓ RSA-4096 KEYPAIR READY - ZERO-KNOWLEDGE ENCRYPTION 🔑 4096-bit RSA Massima sicurezza crittografica 🔐 Zero-Knowledge Server non vede private key 💾 Session Storage In memoria solo durante sessione

4096-bit Strength

RSA-4096 garantisce sicurezza anche contro attacchi futuri. Resistente a quantum computing fino a ~2030.

Private Key Mai in Chiaro

La chiave privata è sempre cifrata con KEK (derivato da password). Server memorizza solo versione cifrata.

Public Key Distribuibile

La chiave pubblica è accessibile a tutti gli utenti per permettere condivisione E2EE.

In-Memory Session

Dopo login, private key decrypted è tenuta in memoria (sessionStorage/variable). Scompare al logout.

API Reference Completo

Documentazione completa di tutti gli endpoint API di ZeroKeep, con esempi di richieste e risposte.

Authentication

POST
/api/register

Registra un nuovo utente con crittografia E2EE.

Request Body:

{
  "email": "user@example.com",
  "password_hash": "pbkdf2_sha256_hash",  // PBKDF2 client-side
  "salt": "base64_encoded_salt",
  "rsa_public_key": "-----BEGIN PUBLIC KEY-----...",
  "rsa_encrypted_private_key": "base64_encrypted_private_key",
  "rsa_private_key_iv": "base64_iv"
}

Response (201 Created):

{
  "message": "User registered successfully",
  "user_id": "uuid-v4",
  "email": "user@example.com"
}

Errors:

400 Bad Request: "Email already exists"
400 Bad Request: "Invalid RSA public key format"
500 Internal Server Error
POST
/api/login

Autentica utente e restituisce JWT token.

Request Body:

{
  "email": "user@example.com",
  "password_hash": "pbkdf2_sha256_hash"
}

Response (200 OK):

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user_id": "uuid-v4",
  "email": "user@example.com",
  "rsa_encrypted_private_key": "base64_encrypted",
  "rsa_private_key_iv": "base64_iv"
}

Errors:

401 Unauthorized: "Invalid credentials"
404 Not Found: "User not found"
POST
/api/logout

Invalida il token JWT corrente.

Headers:

Authorization: Bearer {jwt_token}

Response (200 OK):

{
  "message": "Logged out successfully"
}

File Management

POST
/api/files/upload

Carica un file cifrato con metadata E2EE.

Headers:

Authorization: Bearer {jwt_token}
Content-Type: application/json

Request Body:

{
  "encrypted_file": "base64_encrypted_blob",
  "encrypted_metadata": {
    "encrypted_filename": "base64_encrypted_name",
    "encrypted_filetype": "base64_encrypted_type",
    "file_size_encrypted": 123456,  // Size of encrypted blob
    "iv": "base64_iv",
    "tag": "base64_gcm_tag"
  },
  "encrypted_dek_with_kek": "base64_wrapped_dek",
  "file_hash": "sha256_hex_digest"
}

Response (201 Created):

{
  "file_id": "uuid-v4",
  "s3_path": "tenant_id/file_id.enc",
  "uploaded_at": "2025-12-23T10:30:00Z"
}

Errors:

400 Bad Request: "Missing encrypted metadata"
401 Unauthorized: "Invalid or expired token"
413 Payload Too Large: "File exceeds 5GB limit"
507 Insufficient Storage: "Storage quota exceeded"
GET
/api/files

Elenca tutti i file dell'utente.

Headers:

Authorization: Bearer {jwt_token}

Query Parameters:

?folder_id=uuid           // Filter by folder (optional)
&limit=50                 // Pagination limit (default: 100)
&offset=0                 // Pagination offset (default: 0)
&sort_by=created_at       // Sort field (created_at, size, name)
&order=desc               // Sort order (asc, desc)

Response (200 OK):

{
  "files": [
    {
      "file_id": "uuid-v4",
      "encrypted_metadata": {
        "encrypted_filename": "base64...",
        "encrypted_filetype": "base64...",
        "file_size_encrypted": 123456,
        "iv": "base64...",
        "tag": "base64..."
      },
      "encrypted_dek_with_kek": "base64...",
      "s3_path": "tenant_id/file_id.enc",
      "created_at": "2025-12-23T10:30:00Z",
      "is_favorite": false
    }
  ],
  "total": 42,
  "limit": 50,
  "offset": 0
}
GET
/api/files/:id/download

Scarica un file cifrato.

Headers:

Authorization: Bearer {jwt_token}

Response (200 OK):

{
  "encrypted_file": "base64_blob",
  "encrypted_metadata": { /* ... */ },
  "encrypted_dek_with_kek": "base64...",
  "file_hash": "sha256..."
}

Errors:

404 Not Found: "File not found"
403 Forbidden: "Access denied"
DELETE
/api/files/:id

Elimina un file (da DB e S3).

Headers:

Authorization: Bearer {jwt_token}

Response (200 OK):

{
  "message": "File deleted successfully",
  "file_id": "uuid-v4"
}

Errors:

404 Not Found: "File not found"
403 Forbidden: "Cannot delete shared file"

Sharing (Public & E2EE)

POST
/api/shares/public

Crea un link di condivisione pubblico con password.

Request Body:

{
  "file_id": "uuid-v4",
  "encrypted_dek_share": "base64_dek_wrapped_with_share_password",
  "share_salt": "base64_salt_for_share_password",
  "expiration_days": 7,
  "max_downloads": 10
}

Response (201 Created):

{
  "share_id": "uuid-v4",
  "share_url": "https://onionedcloud.onion/share/uuid-v4",
  "expires_at": "2025-12-30T10:30:00Z"
}
GET
/api/shares/:id

Accesso a file condiviso (richiede password).

Query Parameters:

?password_hash=pbkdf2_hash   // Hash della share password

Response (200 OK):

{
  "encrypted_file": "base64_blob",
  "encrypted_dek_share": "base64...",
  "share_salt": "base64...",
  "encrypted_metadata": { /* ... */ },
  "downloads_remaining": 9
}

Errors:

401 Unauthorized: "Invalid password"
404 Not Found: "Share expired or not found"
429 Too Many Requests: "Max downloads reached"
POST
/api/shares/e2ee

Condividi file E2EE con utente registrato (RSA).

Request Body:

{
  "file_id": "uuid-v4",
  "recipient_email": "recipient@example.com",
  "encrypted_dek_rsa": "base64_dek_encrypted_with_recipient_public_key",
  "permissions": "read"  // Future: "read", "write", "admin"
}

Response (201 Created):

{
  "share_id": "uuid-v4",
  "recipient_id": "uuid-v4",
  "created_at": "2025-12-23T10:30:00Z"
}

Errors:

404 Not Found: "Recipient not found"
400 Bad Request: "Cannot share with yourself"
403 Forbidden: "File already shared with this user"
GET
/api/shares/received

Elenca file condivisi CON ME (E2EE).

Response (200 OK):

{
  "shares": [
    {
      "share_id": "uuid-v4",
      "file_id": "uuid-v4",
      "sender_email": "sender@example.com",
      "encrypted_dek_rsa": "base64...",
      "encrypted_metadata": { /* ... */ },
      "shared_at": "2025-12-23T10:30:00Z",
      "permissions": "read"
    }
  ]
}
DELETE
/api/shares/:id

Revoca una condivisione (solo owner).

Response (200 OK):

{
  "message": "Share revoked successfully"
}

User & Keys Management

GET
/api/users/:email/public-key

Ottiene la chiave pubblica RSA di un utente (per E2EE sharing).

Response (200 OK):

{
  "email": "user@example.com",
  "rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhki...\n-----END PUBLIC KEY-----",
  "user_id": "uuid-v4"
}

Errors:

404 Not Found: "User not found"
GET
/api/keys/private

Recupera la propria chiave privata cifrata (per decrypt dopo login).

Headers:

Authorization: Bearer {jwt_token}

Response (200 OK):

{
  "rsa_encrypted_private_key": "base64_encrypted_private_key_pkcs8",
  "rsa_private_key_iv": "base64_iv"
}
GET
/api/user/storage

Statistiche storage dell'utente.

Response (200 OK):

{
  "used_bytes": 1234567890,
  "quota_bytes": 5368709120,  // 5 GB
  "percentage_used": 23.0,
  "file_count": 42
}

HTTP Status Codes & Errors

Code Status Descrizione
200 OK Richiesta completata con successo
201 Created Risorsa creata con successo (upload, share, registrazione)
400 Bad Request Parametri mancanti o non validi
401 Unauthorized Token JWT mancante, invalido o scaduto
403 Forbidden Accesso negato (non owner, quota superata)
404 Not Found Risorsa non trovata (file, utente, share)
413 Payload Too Large File supera il limite massimo (5GB)
429 Too Many Requests Rate limit raggiunto
500 Internal Server Error Errore server (database, S3, etc.)
507 Insufficient Storage Storage quota utente esaurita
Rate Limiting

Le API implementano rate limiting per prevenire abusi:

  • Authentication: 5 tentativi / 15 minuti per IP
  • File Upload: 100 upload / ora per utente
  • Downloads: 1000 download / ora per utente
  • Public Shares: 50 accessi / ora per share_id

Headers risposta quando rate limit si avvicina:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 23
X-RateLimit-Reset: 1672329600  // Unix timestamp