Tall-Ants LAB · Experiment

Crittografare un database locale con l'aiuto dell'AI

Un esperimento su SQLCipher, gestione delle chiavi, recovery e verifica assistita dal modello AI, nato durante lo sviluppo di un'applicazione desktop locale.

Tall-Ants LAB

Questo contenuto descrive un esperimento tecnico reale. Non è una certificazione di sicurezza né una guida universale alla progettazione crittografica.

L'obiettivo è mostrare, in modo trasparente e didattico, come è stato affrontato un problema reale e come il modello AI ha partecipato alle diverse fasi di analisi, sviluppo e verifica.

Dieci tappe, in qualsiasi ordine tu voglia leggerle

↑ Torna alla mappa

Le basi: come funziona davvero "cifrare qualcosa"

Prima di entrare nell'esperimento vero e proprio, un breve recap dei due modelli su cui si basa tutta la crittografia moderna. Non servono formule complesse: bastano le idee di fondo.

Crittografia simmetrica: una sola chiave

Nel modello simmetrico esiste una sola chiave, usata sia per cifrare sia per decifrare. In forma semplificata:

C = E(K, P) e viceversa P = D(K, C)

dove è il messaggio in chiaro, è il testo cifrato, è la chiave condivisa, e sono le funzioni di cifratura e decifratura.

Chi possiede K può fare entrambe le cose. È lo stesso principio usato da , solo con un algoritmo molto più robusto di quello del prossimo esempio.

PROVA TU · cifrario a sostituzione con chiave (XOR didattico)

Un esempio volutamente semplice, non sicuro, solo per vedere il meccanismo: ogni carattere del messaggio viene combinato con la chiave. La stessa chiave, applicata una seconda volta, riporta il messaggio originale.

Testo cifrato (esadecimale)—
Testo decifrato—

Nota: questo è un XOR a scopo didattico, non un algoritmo sicuro. AES-256-GCM risolve le debolezze di uno schema così semplice (chiave troppo corta, pattern ripetuti, nessuna autenticazione) restando comunque un cifrario a chiave singola.

Crittografia a chiave pubblica: due chiavi, un unico verso

Nel modello asimmetrico esistono due chiavi diverse, generate insieme e legate matematicamente: una , che può essere distribuita a chiunque, e una , che deve restare segreta.

C = E(K_pub, P) ma solo P = D(K_priv, C)

Chi cifra con la chiave pubblica non può decifrare il proprio stesso messaggio: serve la chiave privata corrispondente. È il principio dietro TLS/HTTPS, la firma digitale e la posta cifrata (PGP). Più avanti in questa pagina puoi provarlo con un esempio interattivo reale (RSA su numeri piccoli).

La differenza non è "quale dei due è più sicuro", ma chi deve condividere cosa con chi: la simmetrica presuppone che le parti abbiano già un segreto in comune; l'asimmetrica risolve il problema di stabilirlo, ma è molto più lenta su grandi quantità di dati.

Un database locale che nessuno dovrebbe poter leggere copiandolo

Un'applicazione di contabilità personale può conservare dati particolarmente sensibili: movimenti, saldi, categorie di spesa, pianificazioni e informazioni economiche. Il requisito di partenza era semplice da formulare:

Mantenere il database interamente locale, ma evitare che il file SQLite potesse essere letto semplicemente copiandolo o aprendolo con un normale visualizzatore.

La domanda dietro l'esperimento: come cifrare realmente un database SQLite locale senza trasformare l'applicazione in un sistema dipendente da server, account cloud o connessione Internet?

La soluzione implementata utilizza più livelli, ciascuno con una responsabilità diversa: , una master key casuale a 256 bit, , una Recovery Key separata e + per il recovery.

L'elemento più importante: non esiste una sola "password del database". Esiste una piccola architettura di gestione delle chiavi.

Cifrare il database non basta

È facile pensare alla cifratura così: password → database cifrato. Ma resta una domanda: dove viene conservata la password? Se la chiave fosse salvata accanto al database in chiaro, la cifratura perderebbe gran parte del proprio valore.

Approccio ingenuo
password fissa nel codice o in un file di config
↓
database cifrato
↓
chiave leggibile da chiunque acceda al PC

La cifratura c'è, ma non protegge da un attaccante che ha accesso al filesystem: la chiave è lì, accanto al file.

Approccio adottato
MASTER KEY casuale a 256 bit
↓
database cifrato con SQLCipher
↓
master key protetta da Windows DPAPI

Il problema si separa in due livelli indipendenti: cifrare i dati, e proteggere separatamente chi apre quella cifratura.

La gestione delle chiavi, livello per livello

Ogni livello ha una responsabilità precisa e non conosce i dettagli implementativi degli altri.

1

Master key casuale

Sequenza casuale di 32 byte (256 bit), generata con . Non deriva da nome utente, PC, Machine ID o password prevedibile.

32 byte256 bitCSPRNG di sistema
↓ applicata a
2

SQLCipher

Sostituisce il comportamento normale di SQLite con un database cifrato. Nel percorso di produzione la chiave viene applicata direttamente alla connessione tramite sqlite3_key, non tramite PRAGMA key. Segue un self-check con SELECT count(*) FROM sqlite_master;: se la chiave non è corretta, l'apertura fallisce.

sqlite3_key APIself-check schema
↓ custodita da
3

Windows DPAPI

La master key viene protetta con CryptProtectData, legato all'utente Windows corrente, ed evita di conservarla in chiaro nel filesystem. Per riaprire il database si usa CryptUnprotectData.

scope utente correntenessuna password da ricordare
↓ percorso alternativo
4

Recovery Key

Non cifra il database: protegge la stessa master key, per il caso in cui DPAPI non sia più disponibile o serva accedere da un altro ambiente.

indipendente da DPAPIprotegge la master key, non i dati
↓ tramite
5

Argon2id + AES-256-GCM

La Recovery Key diventa una chiave crittografica tramite (64 MiB, 3 iterazioni, parallelismo 1, salt 16 byte, output 32 byte). Quella chiave cifra la master key con , che offre anche autenticazione: rileva se il dato cifrato è stato alterato.

64 MiB memoria3 iterazioniautenticato (GCM)

Le parti più interessanti, riga per riga

Estratti semplificati dal codice analizzato durante l'assessment, uno per ciascun passaggio dell'architettura a strati vista sopra. Sono riscritti per leggibilità didattica, non copiati riga per riga dal repository.

1 · Generare la master key

32 byte casuali dalla sorgente di casualità del sistema operativo, non da una libreria di terze parti.

rust
use windows::Win32::Security::Cryptography::BCryptGenRandom;

let mut master_key = [0u8; 32];
BCryptGenRandom(None, &mut master_key, BCRYPT_USE_SYSTEM_PREFERRED_RNG)?;
// master_key ora contiene 256 bit di entropia del sistema operativo

La riga evidenziata è l'unico posto in cui la master key viene creata: da qui in poi viene solo protetta, mai rigenerata.

2 · Applicare la chiave a SQLCipher

La chiave viene passata direttamente alla connessione, non tramite comando SQL — e viene subito verificata con un self-check.

rust
ffi::sqlite3_key(
    connection.handle(),
    key.as_bytes().as_ptr().cast(),
    key.as_bytes().len() as i32,
);

connection.query_row("SELECT count(*) FROM sqlite_master", [], |r| r.get::<_, i64>(0))?;
// se la chiave è sbagliata, questa query fallisce: è il self-check

Senza questo self-check, una chiave sbagliata aprirebbe comunque una connessione "valida" ma illeggibile — l'errore emergerebbe solo molto più tardi, in un punto meno chiaro del codice.

3 · Proteggere la master key con DPAPI

rust
use windows::Win32::Security::Cryptography::CryptProtectData;

let protected_blob = CryptProtectData(&master_key_blob, None, None, None, None, flags)?;
fs::write(vault_path, &protected_blob)?;
// il blob è leggibile solo dall'utente Windows corrente

Da qui in poi il file su disco non contiene mai la master key in chiaro: contiene un blob che Windows sa aprire solo per lo stesso account che lo ha creato.

4 · Derivare una chiave dalla Recovery Key con Argon2id

rust
use argon2::{Argon2, Params};

let params = Params::new(64 * 1024, 3, 1, Some(32))?; // memoria KiB, iterazioni, parallelismo
let argon2 = Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params);

let mut derived_key = [0u8; 32];
argon2.hash_password_into(recovery_key.as_bytes(), &salt, &mut derived_key)?;

I parametri evidenziati sono esattamente quelli che puoi muovere nel simulatore più sotto.

5 · Cifrare la master key per il recovery, con AES-256-GCM

rust
use aes_gcm::{Aes256Gcm, Nonce, aead::Aead};

let cipher = Aes256Gcm::new_from_slice(&derived_key)?;
let encrypted_master_key = cipher.encrypt(nonce, master_key.as_ref())?;
// encrypted_master_key include anche il tag di autenticazione GCM

Se qualcuno modifica anche un solo byte di encrypted_master_key, il tag di autenticazione GCM fa fallire la decifratura invece di restituire dati corrotti silenziosamente.

Crittografia a chiave pubblica: un esempio reale su numeri piccoli

Questa sezione è un'incursione volutamente separata dall'esperimento sul database: non riguarda SQLCipher, ma il secondo grande modello di crittografia citato più sopra, per poterlo confrontare davvero invece che a parole.

Lo scenario classico per capirla: Alice vuole mandare un messaggio a Bob attraverso un canale che Trudy può intercettare. Bob non ha mai incontrato Alice prima, quindi non possono essersi già accordati su una chiave segreta condivisa — il problema che la crittografia simmetrica, da sola, non risolve. Il messaggio che proveremo a trasferire è semplicemente una piccola parola scritta da Alice; nella pratica reale sarebbe più spesso una chiave simmetrica da consegnare in modo sicuro, non un messaggio lungo.

In questo esperimento sul database, questo problema non esiste: non c'è nessuna Alice e nessun Bob, nessun canale da attraversare. Master key e Recovery Key restano sempre sulla stessa macchina. È per questo che l'architettura vista finora non ha bisogno di chiavi pubbliche — il paragrafo finale di questa sezione spiega perché.

Da dove vengono n, e e d — passo per passo

Non è una sequenza arbitraria: ogni passaggio esiste per una ragione matematica precisa, ed è esattamente qui che si inserisce il generatore interattivo qui sotto, per far vedere anche cosa succede quando un passaggio viene violato.

1

Scegli due numeri p e q, tenuti segreti

Perché primi, e non due numeri qualsiasi? Perché tutto lo schema si regge su un compito facile in un verso e difficile nell'altro: moltiplicare due primi per ottenere n è immediato; tornare indietro da n ai due fattori originali (fattorizzare) è impraticabile se i numeri sono abbastanza grandi. Con fattori non primi questa asimmetria si indebolisce o smette del tutto di reggere il ragionamento dei passaggi successivi.

↓
2

Calcola n = p × q

n diventa il modulo pubblico: la base su cui si svolge tutta l'aritmetica di cifratura e decifratura. Può essere conosciuto da chiunque — è la segretezza di p e q presi separatamente, non quella di n, a proteggere lo schema.

↓
3

Calcola φ(n) = (p−1) × (q−1)

Prima la versione senza gergo: è semplicemente "quanti numeri, contati da 1 fino a n−1, non hanno nessun fattore in comune con n". Prova con un esempio piccolo prima di leggere la formula: scegli n qui sotto e guarda quali numeri restano verdi.

I numeri verdi sono quelli coprimi con n — nessun fattore in comune. Contali: con n = 15 = 3 × 5 sono esattamente 8, e (3−1)×(5−1) = 2×4 = 8. Non è una coincidenza: quando n è il prodotto di due primi distinti, questo conteggio si scompone sempre in quel prodotto — una proprietà dimostrata, non una regola empirica che capita di funzionare.

Perché ci serve: esiste un teorema (di Eulero) secondo cui elevare un numero coprimo con n alla potenza φ(n) dà sempre resto 1 dividendo per n — una specie di "giro completo" nell'aritmetica modulare. Applicata due volte, una in cifratura e una in decifratura, è esattamente questa identità a far tornare il messaggio originale. E calcolare φ(n) è facile se conosci p e q, ma resta difficile quanto fattorizzare n se conosci solo n: la stessa porta a senso unico vista al passo 1.

↓
4

Scegli e, con φ(n)

"Coprimo" vuol dire che e e φ(n) non condividono nessun fattore comune maggiore di 1 — il loro massimo comun divisore (MCD, o gcd) è 1. Serve perché al passo successivo dobbiamo trovare l'inverso moltiplicativo di e modulo φ(n): un inverso del genere esiste solo se i due numeri sono coprimi.

In pratica e viene spesso scelto piccolo e fisso — 65537 è un valore comune in RSA reale, qui usiamo 17 per restare su numeri piccoli — perché rende la cifratura più veloce; poi si verifica che sia davvero coprimo con il φ(n) ottenuto. Se non lo è, si prova un altro e: non è un valore che deve avere una forma speciale, deve solo superare questo controllo.

↓
5

Calcola d, l'inverso di e modulo φ(n)

d è l'unico numero tale che e × d, diviso per φ(n), lascia resto 1 — in simboli, e × d ≡ 1 (mod φ(n)). Si calcola con l', una variante dell'algoritmo per trovare il MCD che restituisce anche i coefficienti per esprimerlo come combinazione di e e φ(n): da lì si ricava direttamente l'inverso.

È questo il passaggio che rende d calcolabile solo da chi conosce φ(n) — quindi solo da chi conosce p e q separatamente, non solo il loro prodotto n.

Esempio numerico completo con p = 61, q = 53:

n = 61 × 53 = 3233 φ(n) = 60 × 52 = 3120 e = 17  (gcd(17, 3120) = 1 → va bene) d = 2753  (l'inverso di 17 modulo 3120)
Verifica: 17 × 2753 = 46.801, e 46.801 mod 3120 = 1. È esattamente questa proprietà — non una convenzione — a far funzionare la coppia di chiavi.

Cosa viaggia dove: Bob pubblica (e, n) — chiunque, incluso Trudy, può vederli. (d, n) resta solo con Bob. Alice usa (e, n) per cifrare; solo (d, n) può decifrare.

Prova tu: genera la tua coppia di chiavi

Cambia p, q ed e qui sotto. Prova prima con due primi diversi da quelli dell'esempio (per esempio p = 7, q = 11), poi prova a rompere deliberatamente lo schema con p = 9, q = 11, e = 7 — 9 non è primo — e osserva che il messaggio decifrato non torna più leggibile, anche usando "la chiave giusta" generata da quei numeri.

Nota onesta: con alcune combinazioni di numeri non primi il messaggio può tornare leggibile lo stesso, per pura coincidenza aritmetica — non perché la regola "servono due primi" sia sbagliata, ma perché piccoli numeri creano occasionalmente casi particolari. Con numeri delle dimensioni usate da RSA reale questa coincidenza è irrilevante: è un effetto visibile solo perché qui i numeri sono volutamente piccoli.

GENERATORE RSA · scegli p, q, e — poi cifra e decifra con le chiavi ottenute
n = 3233 · φ(n) = 3120 · gcd(e, φ(n)) = 1 ✓ · d = 2753 ✓
Chiave pubblica di Bob (e, n)
e = 17, n = 3233
Chiave privata di Bob (d, n)
d = 2753, n = 3233
Cosa vede Trudy sul canale (un numero per carattere)
—
Cosa legge chi decifra
—

Se scegli p o q non primi, il generatore calcola comunque n e (p−1)(q−1) per farti vedere il risultato — ma quel numero non è la vera φ(n), quindi anche con la decifratura "giusta" il messaggio che torna indietro non sarà quello di partenza: è la dimostrazione pratica del passo 1. Se scegli numeri molto piccoli, anche con p e q primi il messaggio può degradarsi perché più caratteri finiscono per condividere lo stesso resto modulo n — un limite di questo schema-giocattolo con l'alfabeto ASCII, non di RSA in sé.

Un problema che RSA da solo non risolve: l'autenticità della chiave

C'è una domanda che la matematica qui sopra non copre: quando Bob invia (e, n) ad Alice attraverso lo stesso canale insicuro, come fa Alice a essere sicura che quella chiave sia davvero di Bob e non di Trudy, che si è finta Bob e ha spedito la propria coppia al posto suo?

RSA da solo non risponde a questa domanda: garantisce che solo chi possiede d possa decifrare, ma non dice nulla su chi possieda quel d. Se Trudy riesce a far credere ad Alice che la sua chiave pubblica sia quella di Bob, Alice cifrerà un messaggio che Trudy può leggere tranquillamente — un attacco detto man-in-the-middle.

Nella pratica il problema si risolve legando la chiave pubblica a un'identità tramite una terza parte fidata (un certificato digitale, firmato da una Certificate Authority — è il meccanismo dietro il lucchetto HTTPS del browser), oppure verificando l'impronta della chiave attraverso un canale diverso e già fidato.

Questo è esattamente il tipo di problema che l'esperimento sul database non deve mai affrontare: master key, DPAPI e Recovery Envelope restano sulla stessa macchina, senza mai attraversare un canale dove qualcun altro potrebbe sostituirsi a un interlocutore. Non serve autenticare una chiave che non lascia mai il dispositivo.

Perché SQLCipher non usa questo schema. Anche a parte il problema dell'autenticità, RSA cifra pochi byte alla volta ed è ordini di grandezza più lento di AES su grandi quantità di dati: cifrare un intero database riga per riga con RSA sarebbe impraticabile. Nei sistemi reali (TLS, PGP) la chiave pubblica/privata non cifra i dati stessi: cifra o scambia in modo sicuro una chiave simmetrica, che poi fa il lavoro pesante — lo stesso ruolo che, nella nostra architettura, è giocato da DPAPI e da Argon2id: proteggono la master key simmetrica, non la sostituiscono con una coppia di chiavi.

Simmetrica — usata qui

Una chiave sola. Velocissima anche su un intero database. Richiede solo un modo sicuro per custodire quella chiave (DPAPI, Recovery Key) — nessun canale, nessun problema di autenticità.

Asimmetrica — non usata qui

Due chiavi legate matematicamente. Utile per scambiare un segreto tra parti che non si conoscono già (TLS, firma digitale), ma solo se l'autenticità della chiave pubblica è garantita da qualcos'altro (un certificato). Troppo lenta per cifrare l'intero contenuto di un database locale.

Dove si usa davvero: banca, browser, certificati

Lo scenario Alice/Bob non è astratto: è letteralmente quello che succede ogni volta che apri il sito della tua banca. Il browser (Alice) deve mandare dati sensibili alla banca (Bob) attraverso una rete che considera insicura per definizione.

1
La banca espone un certificatocontiene la sua chiave pubblica (e, n) e la sua identità, firmati da una Certificate Authority — è lo "scambio di certificati" che il browser fa all'inizio della connessione HTTPS.
2
Il browser verifica la firmacontrolla che il certificato sia firmato da una CA di cui si fida, risolvendo così il problema dell'autenticità visto sopra — senza questo passo, saresti esposto esattamente al man-in-the-middle descritto poco fa.
3
Il browser genera una chiave simmetricacasuale, valida solo per questa sessione, e la cifra con la chiave pubblica della banca prima di spedirla.
4
Solo la banca può recuperarlacon la sua chiave privata. Da questo momento, tutto il traffico (saldo, bonifici, credenziali) viaggia cifrato con quella chiave simmetrica — di nuovo lo schema "ibrido" già visto: la chiave pubblica serve solo a scambiare in sicurezza la chiave che farà il lavoro pesante.

La proprietà di sicurezza chiave, detta con precisione: non è che chi cifra "non ha alcun segreto" — deve comunque generare la chiave di sessione senza farsela rubare prima di spedirla. La proprietà reale è che la chiave pubblica stessa non ha bisogno di restare segreta: la banca può stamparla su un cartellone, chiunque può vederla, e questo non aiuta nessuno a decifrare nulla. È l'opposto della crittografia simmetrica, dove chiunque ottenga la chiave può sia cifrare sia decifrare, e quindi entrambe le parti devono proteggerla allo stesso modo. Con la chiave pubblica, solo un lato — quello che detiene la chiave privata — deve proteggere un segreto.

Le principali minacce a un sistema a chiave pubblica

Nessuna di queste minacce riguarda "la matematica di RSA in sé essere sbagliata" — riguardano tutte come le chiavi vengono generate, distribuite o usate.

Chiave pubblica sostituita (man-in-the-middle)

Visto sopra: se l'autenticità della chiave pubblica non è verificata (certificato falso o non controllato), un intermediario può farsi passare per il destinatario legittimo.

Fattorizzazione del modulo n

Se n è troppo corto, un attaccante può risalire a p e q e ricostruirsi la chiave privata da solo. Quanto è pratico dipende interamente dalla dimensione di n — è il tema della prossima sottosezione.

Numeri primi generati male

Se p e q non sono scelti con un generatore di numeri casuali davvero imprevedibile, o sono troppo vicini fra loro, esistono scorciatoie matematiche (come il metodo di fattorizzazione di Fermat) molto più veloci della fattorizzazione generica.

Padding e implementazione scorrette

Alcuni attacchi storici famosi (come quello di Bleichenbacher) non rompono la matematica di RSA: sfruttano errori nel modo in cui un'implementazione gestisce i messaggi cifrati male formati, per estrarre informazioni un bit alla volta.

Attacchi side-channel

Misurano effetti collaterali dell'hardware — tempo di calcolo, consumo energetico, emissioni elettromagnetiche — durante l'uso della chiave privata, per dedurne i bit senza mai fattorizzare nulla.

Computer quantistici (minaccia futura)

L'algoritmo di Shor, se eseguito su un computer quantistico abbastanza grande, fattorizzerebbe n in tempi brevi. Nessun computer quantistico oggi esistente si avvicina a questa capacità su chiavi di dimensione reale — è per questo che si sta già lavorando a schemi "post-quantistici" alternativi.

Quanto è difficile fattorizzare n, in pratica

Il modo più semplice per rompere RSA partendo dalla sola chiave pubblica (e, n) è la divisione successiva: provare a dividere n per 2, poi per 3, poi per 5... finché uno dei tentativi non lascia resto zero. Il primo che funziona è p; da lì q = n / p si ottiene gratis. È anche il metodo più lento possibile: gli attacchi reali usano algoritmi molto più furbi — in particolare il crivello generale dei campi di numeri (GNFS) — ma restano comunque governati dalla stessa idea di fondo: il tempo esplode al crescere delle cifre di n.

FORZA n · divisione successiva a partire dalla chiave pubblica
Scegli un n e premi "Fattorizza".

Il tentativo si ferma da solo dopo alcuni milioni di divisioni o dopo qualche secondo, per non bloccare la pagina — è già abbastanza per sentire la differenza fra "istantaneo" e "già scomodo" restando su numeri piccolissimi.

E se più computer lavorano insieme?

Nessun attaccante userebbe davvero un solo processore su numeri di queste dimensioni. Si dividerebbe lo spazio dei possibili divisori fra molte macchine, ciascuna responsabile di una fetta — esattamente come è successo per davvero con RSA-768, distribuito su centinaia di macchine per quasi due anni. Qui puoi vederlo in scala minuscola: stesso principio, stesso tipo di n, ma la ricerca viene divisa fra più "macchine" simulate.

ATTACCO DISTRIBUITO · dividi lo spazio di ricerca fra più macchine
Scegli quante macchine simulare e premi "Avvia attacco distribuito".

Le macchine sono simulate a turno in un solo browser (non c'è vero multi-core qui), ma ognuna analizza solo la propria fetta dello spazio di ricerca — proprio come farebbero macchine reali indipendenti. Con 1 sola macchina questo n richiede un paio di secondi; prova a salire a 8 o 64 e guarda il tempo scendere.

Le chiavi RSA reali non usano moduli come questi: ne usano di centinaia di cifre decimali. Ecco cosa raccontano le fattorizzazioni realmente completate finora, con l'algoritmo più veloce oggi conosciuto — non la divisione successiva:

1991

RSA-100 (100 cifre decimali): fattorizzato entro il 1° aprile 1991, nel giro di settimane con i mezzi dell'epoca.

1999

RSA-155 (512 bit): la fattorizzazione richiese circa 4.000 anni-MIPS di calcolo distribuito, completata in alcuni mesi coordinando più centri di calcolo.

2010

RSA-768 (768 bit, 232 cifre): il solo crivello, su un singolo processore dell'epoca, avrebbe richiesto circa millecinquecento anni; distribuito su centinaia di macchine, l'intero sforzo durò circa due anni e mezzo.

Mai fattorizzato

RSA-1024: gli stessi autori della fattorizzazione di RSA-768 hanno stimato che fattorizzare un modulo a 1024 bit sarebbe circa mille volte più difficile; a oggi nessuna fattorizzazione pubblica di un numero fra RSA-704 e RSA-2048 è mai stata completata.

Standard attuale

RSA-2048, la dimensione oggi raccomandata: la difficoltà cresce di un fattore ancora enormemente più grande rispetto a RSA-1024 — ben oltre quanto i computer classici attuali possano affrontare in tempi utili.

Nota: i computer quantistici, se abbastanza potenti, cambierebbero questo quadro grazie all'algoritmo di Shor citato sopra tra le minacce — motivo per cui la ricerca su schemi "post-quantistici" è già attiva oggi, non in previsione di un problema lontanissimo nel tempo.

Come i parametri di Argon2id cambiano il compromesso sicurezza/attesa

Argon2id trasforma la Recovery Key in una chiave crittografica facendola "costare" tempo e memoria: più costa a te aprire il database una volta, più costa a un attaccante provare miliardi di password. Muovi i controlli per vedere il compromesso — è una simulazione didattica, non una misura di sicurezza reale.

SIMULATORE · Argon2id (parametri illustrativi)
Attesa percepita all'apertura~0.3s
⚠ ESEMPIO ILLUSTRATIVO — non un benchmark hardware reale né un audit di sicurezza. I numeri qui sotto servono solo a mostrare la direzione dell'effetto (più costa a te, più costa a chi attacca), non un valore certificato o misurato.
Tentativi al secondo stimati per un attaccante (una singola macchina dedicata) —
Spazio di tentativi ipotizzato: ~10.000 miliardi di combinazioni plausibili 10.000.000.000.000
Tempo stimato per esaurirlo, macchina singola —
Stesso attacco, parallelizzato su un cluster di 10.000 GPU —
Con questi parametri l'apertura è quasi istantanea, ma un attaccante con hardware dedicato può tentare molte password al secondo su un dump della Recovery Envelope.

Nota: le curve usate qui sono semplificate a scopo didattico, per mostrare la direzione del compromesso (memoria e iterazioni più alte → apertura più lenta ma attacco più costoso). Non rappresentano un benchmark reale di Argon2id, né una stima calibrata su hardware d'attacco esistente.

Tre percorsi, una sola master key

Clicca sui passaggi per seguire cosa succede in ciascuno dei tre scenari.

Genera MASTER KEY casuale

Migrare un database già esistente

L'applicazione esisteva già con un database SQLite non cifrato. Il processo non modifica brutalmente il file originale.

1
Backup verificatodel database SQLite in chiaro, prima di ogni operazione.
2
Database temporaneo SQLCiphercreato accanto all'originale.
3
sqlcipher_export(...)copia i dati nel nuovo database cifrato.
4
Verifiche di integritàconfronto di schema, versione, tabelle, conteggio righe, più PRAGMA integrity_check.
5
Swap dei fileil database cifrato prende il posto di quello in chiaro.
6
Vecchio DB mantenuto come rollbackpensionato solo dopo un backup moderno verificato e un recovery envelope valido.
La sicurezza non riguarda soltanto l'algoritmo crittografico, ma anche il modo in cui si migra, si verifica e si può tornare indietro in caso di errore.

Quattro ruoli separati, non uno solo

L'AI non è stata trattata come una funzione che "scrive codice e basta". La separazione dei ruoli riduce un rischio tipico dello sviluppo assistito dall'AI: chiedere allo stesso modello di implementare qualcosa e considerare automaticamente corretta la propria implementazione.

Co-progettista

Aiuta a scomporre il problema — proteggere il database, proteggere la chiave, gestire recovery e migrazione — e a confrontare alternative architetturali.

Coding agent

Implementa modifiche circoscritte nel repository, in sprint limitati: analisi iniziale → piano → patch limitata → test → verifica.

Reviewer

Rilegge il codice in modalità read-only, senza modificarlo, e ricostruisce ciò che il codice fa realmente — distinguendo osservato da dedotto.

Tutor

Trasforma il codice risultante in una spiegazione comprensibile, come questa stessa pagina.

Il controllo finale torna sempre al codice, ai test e alle evidenze — mai alla parola del modello su se stesso.

Cinque principi

1
Cifrare i dati e gestire le chiavi sono due problemi differenti.SQLCipher protegge il database, ma serve comunque una strategia per custodire la chiave.
2
La recovery deve essere progettata insieme alla cifratura.Una protezione perfetta che rende impossibile recuperare legittimamente i propri dati può essere inutilizzabile.
3
La migrazione è parte della sicurezza.Backup, validazione, rollback e cancellazione controllata del plaintext fanno parte del modello complessivo.
4
L'AI può accelerare lo sviluppo, ma deve lavorare con ruoli e confini chiari.Progettazione, coding e review sono attività diverse, con esiti diversi da confrontare.
5
La documentazione deve essere verificata contro il codice.Anche una descrizione sostanzialmente corretta può differire in dettagli importanti dall'implementazione reale.
Un modello AI può partecipare a un processo tecnico complesso assumendo ruoli differenti, purché ogni risultato venga riportato a evidenze verificabili nel codice e nei test.
RustTauriSQLiteSQLCipherWindows DPAPIBCryptGenRandomArgon2idAES-256-GCM

Questo esperimento ti è stato utile per capire il funzionamento?

Hai un'esperienza diversa da raccontare, un errore, un'obiezione o un'idea da aggiungere?

Invia un contributo →
La descrizione tecnica di questa pagina deriva da un assessment read-only del codice. I comportamenti che richiedono misurazioni runtime non sono presentati come verificati.