Vai al contenuto

Funzioni hash crittografiche

Una guida incentrata sulla verifica delle funzioni hash crittografiche, che tratta proprietà di sicurezza, codifica dei byte, SHA-2, SHA-3, Keccak, impieghi nelle blockchain e rischi di implementazione.

Aggiornato

Solo a scopo educativo; non costituisce consulenza di investimento né di implementazione crittografica. La corrispondenza di un digest non dimostra, da sola, autenticità, proprietà, autorizzazione, finalità o disponibilità dei dati.

Risposta diretta

Una funzione hash crittografica associa in modo deterministico un messaggio rappresentato come byte a un digest con una lunghezza di output definita. Per un hash di lunghezza fissa di n bit, la relazione fondamentale è:

h = H(m), where h is in {0,1}^n

Gli stessi byte e lo stesso algoritmo producono il medesimo digest. La modifica di un solo bit dell’input dovrebbe alterare in modo imprevedibile molti bit dell’output, ma questo effetto valanga non costituisce la definizione di sicurezza. I principali obiettivi di sicurezza sono la resistenza alla preimmagine (dato un digest, è impraticabile trovare un input che lo produca), la resistenza alla seconda preimmagine (dato un input, è impraticabile trovarne uno diverso con lo stesso digest) e la resistenza alle collisioni (è impraticabile trovare due input distinti qualsiasi con lo stesso digest).

L’hashing non è cifratura: non esiste una chiave di decifratura, né vi è garanzia che l’input possa essere recuperato. Poiché un numero infinito di messaggi possibili viene associato a uno spazio di output finito, le collisioni devono esistere; sicurezza significa che trovarne una utilizzabile è computazionalmente impraticabile per l’algoritmo e la lunghezza di output scelti.

Un digest, inoltre, non fornisce autenticità da solo. Ricalcolare l’hash di un file rileva una mancata corrispondenza soltanto quando il digest atteso e l’algoritmo sono stati ottenuti tramite un canale affidabile. I protocolli conseguono garanzie più solide combinando gli hash con firme, codici di autenticazione dei messaggi, strutture dati autenticate, regole di consenso o proof of work.

Come funziona

  1. Definire i byte esatti. La codifica del testo, l’uso di maiuscole e minuscole, gli spazi, l’ordine dei campi, la rappresentazione degli interi, i prefissi di lunghezza e la serializzazione incidono tutti su m. Un protocollo deve specificare una codifica canonica e vincolare l’hash a un algoritmo, una versione, una rete e uno scopo.
  2. Eseguire la costruzione specificata. SHA-256 preelabora un messaggio di lunghezza limitata, lo divide in blocchi e aggiorna iterativamente uno stato interno. SHA3-256 utilizza una costruzione sponge basata su KECCAK. Entrambe restituiscono digest di 256 bit, ma sono funzioni diverse e non producono output intercambiabili.
  3. Interpretare la sicurezza in base alla proprietà richiesta. Per un hash ideale di n bit, una ricerca generica della preimmagine richiede circa 2^n valutazioni, mentre una ricerca generica di collisioni ne richiede circa 2^(n/2) a causa del paradosso del compleanno. La sola lunghezza dell’output non è sufficiente se l’algoritmo è compromesso, il digest è troncato o il protocollo circostante è difettoso.
  4. Costruire il protocollo intorno al digest. Uno schema di firma digitale può firmare il digest di un messaggio; HMAC aggiunge una chiave segreta per l’autenticazione dei messaggi; un albero di Merkle vincola molte foglie a un’unica radice; e il proof of work calcola ripetutamente l’hash di intestazioni di blocco candidate finché un digest non soddisfa un obiettivo. Queste costruzioni offrono garanzie diverse.
  5. Utilizzare la funzione esatta della blockchain. Le intestazioni dei blocchi e i nodi Merkle di Bitcoin usano un doppio SHA-256 nell’ordine dei byte specificato. L’esecuzione di Ethereum utilizza Keccak-256, derivato dal progetto KECCAK precedente alla standardizzazione, e non lo SHA3-256 standardizzato. Un’etichetta come “hash a 256 bit” non è quindi sufficiente per la verifica.
  6. Verificare il contesto prima di attribuire un significato. Occorre controllare la fonte del digest atteso, l’identificatore dell’algoritmo, la codifica dei byte, il dominio o la blockchain, il riferimento al blocco e allo stato, lo stato delle conferme e qualsiasi troncamento. Un calcolo corretto eseguito nel contesto sbagliato resta una verifica fallita.

Esempi pratici

  • Una minima modifica dell’input. Lo SHA-256 dei cinque byte UTF-8 di hello è 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824. Sostituendo il primo byte con la H maiuscola si ottiene 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969. I digest differenti non rivelano quale byte sia cambiato.
  • La robustezza della sicurezza non coincide con la lunghezza del digest in ogni modello di attacco. Un hash ideale a 256 bit offre circa 2^256 operazioni di resistenza alla preimmagine, ma 2^128 contro le collisioni. Questa distinzione è importante quando un protocollo dipende dalla resistenza alle collisioni, come spesso accade nei flussi di firma digitale.
  • Una prova di Merkle autentica l’inclusione rispetto a una singola radice. Il verificatore calcola l’hash della foglia codificata con ciascun nodo fratello fornito, nell’ordine specificato, finché non ricostruisce la radice vincolata. Una corrispondenza non dimostra che la radice sia definitiva, che i dati della foglia siano veri o che i dati omessi siano disponibili.
  • Il proof of work aggiunge una regola relativa all’obiettivo. Bitcoin convalida un’intestazione candidata solo quando il suo valore di doppio SHA-256, interpretato secondo le regole di consenso, è minore o uguale all’obiettivo codificato. Il digest non diventa più resistente alle collisioni perché i miner hanno svolto più lavoro.

Rischi

  • Utilizzare un algoritmo obsoleto o inadatto, in particolare affidarsi a SHA-1 quando è richiesta la resistenza alle collisioni.
  • Trattare SHA3-256, Keccak-256, SHA-256, il doppio SHA-256 e varianti troncate in modi diversi come intercambiabili.
  • Calcolare l’hash del testo visualizzato anziché dei byte canonici, oppure trascurare la normalizzazione Unicode, gli spazi, l’ordine dei byte, l’ordine dei campi e la codifica delle lunghezze.
  • Scaricare un file e il relativo digest atteso dalla stessa posizione compromessa, senza ottenere alcun controllo di integrità indipendente.
  • Utilizzare direttamente un hash generico veloce per memorizzare le password, invece di uno schema di hashing delle password con salt, appositamente progettato e dotato di un fattore di lavoro adeguato.
  • Utilizzare H(secret || message) come codice di autenticazione improvvisato; alcune costruzioni hash iterative consentono attacchi di estensione della lunghezza, mentre HMAC è progettato per l’autenticazione con chiave.
  • Troncare i digest senza calcolare la conseguente sicurezza contro collisioni e preimmagini per la scala e il modello di minaccia del protocollo.
  • Riutilizzare una codifica tra protocolli senza separazione dei domini, consentendo che un digest valido in un contesto venga interpretato in un altro.
  • Presumere che l’hash di una transazione dimostri conferma, finalità, esecuzione riuscita, proprietà o assenza di riorganizzazioni della blockchain.
  • Presumere che un hash del contenuto renda recuperabili i dati a cui fa riferimento; un impegno può rimanere valido anche se ogni copia disponibile scompare.
  • Confrontare stringhe visualizzate dagli explorer senza controllare l’ordine dei byte, le regole sui prefissi, la serializzazione o se l’interfaccia mostra in modo diverso un identificatore interno.
  • Implementare primitive crittografiche senza vettori di test standard, librerie mantenute, revisione indipendente e procedure di aggiornamento.

Errori comuni

  • Un hash è un dato cifrato. La cifratura è reversibile con la chiave appropriata; un hash crittografico è un digest unidirezionale privo di un’operazione di decifratura.
  • Input diversi non possono mai condividere un digest. Le collisioni esistono necessariamente per un output di lunghezza fissa. Un progetto sicuro le rende impraticabili da trovare e sfruttare.
  • Un digest di 256 bit offre sempre 256 bit di sicurezza. Per un hash ideale di 256 bit, la resistenza generica alle collisioni è di circa 128 bit, e le scelte del protocollo possono ridurla ulteriormente.
  • Hash corrispondenti dimostrano chi ha creato il messaggio. Un semplice hash non contiene alcun segreto e non autentica alcun mittente; quando l’origine è rilevante, occorre utilizzare una firma o un MAC adeguato.
  • Keccak-256 e SHA3-256 sono due nomi della stessa funzione. Utilizzano progetti strettamente correlati, ma parametri di standardizzazione diversi, e producono digest differenti.
  • L’hash di una transazione on-chain ne dimostra il regolamento. Identifica i dati codificati della transazione; inclusione nella blockchain, stato dell’esecuzione, conferme e finalità sono fatti distinti.

Argomenti correlati

Fonti

Navigazione

Cerca nella wiki...