Fiat blockchain

Dalla proprietà alla sicurezza

Escher — Stelle

Nei precedenti post di questa serie abbiamo introdotto e presentato:

focalizzando l’attenzione di ogni articolo su Hyperledger Fabric e sulle sue possibili applicazioni nel mondo business/enterprise.

In questo post, che costituisce a tutti gli effetti la seconda parte di

sezioneremo i concetti per scoprire come è fatta una blockchain trattando, fin dove possibile, in dettaglio gli aspetti tecnologi da cui prende vita.

Abbiamo un’idea delle relazioni che intercorrono tra trust, integrità, sistemi peer-to-peer e blockchain:

  • Un sistema -rete- peer-to-peer è costituito da risorse computazionali messe a disposizione dai propri utenti
  • La rete usa Internet per connettere i singoli nodi
  • Non è possibile conoscere il numero di nodi e la loro effettiva affidabilità
  • L’obiettivo fissato è “gestire e garantire la proprietà” di un asset digitale (e non)

Anche qui, come già fatto nel post citato in precedenza, procederemo per piccoli passi: l’approccio che seguiremo sarà quello di immaginare che effettivamente la blockchain non esista ancora e che il nostro scopo sia quello di “creare” un sistema che possa gestire la proprietà in una rete peer-to-peer aperta e non affidabile a priori.

Poniamoci queste domande e proviamo a rispondere:

  1. Come possiamo descrivere la proprietà?
  2. Come possiamo proteggere la proprietà?
  3. Come possiamo “conservare” i dati delle transazioni?
  4. Come possiamo garantire l’integrità di questi dati una volta che saranno distribuiti sulla rete?

Come possiamo descrivere la proprietà?

Se l’obiettivo che ci poniamo è quello di realizzare un sistema, che chiameremo blockchain, che ci permetta di gestire la proprietà -ownership-, la prima cosa da fare, senza alcun dubbio, è quella di decidere come descrivere la proprietà appunto. La transazioni sembrano essere “un buon modo” per delineare ogni passaggio di proprietà e la storia di tutte le transazioni un altrettanto ottimo strumento per identificare l’attuale proprietario.

Come possiamo proteggere la proprietà?

Descrivere la proprietà è solo il punto di partenza: ci serve un modo per evitare che altri possano accedere alla nostra proprietà e che noi possiamo accedere a quella degli altri…La stessa cosa che si fa più o meno nella vita di tutti i giorni usando le porte e magari chiudendole a chiave.

Ci viene in soccorso la crittografia che nel mondo digitale svolge all’incirca lo stesso compito delle porte chiuse a chiave.

In sostanza, informaticamente parlando, la protezione della proprietà si riduce a tre aspetti:

  • Identificare il proprietario
  • Autenticare il proprietario
  • Restringere l’accesso ai solo autorizzati

Vedremo come questi passi poggino le basi sui valori hash.

Come possiamo “conservare” i dati delle transazioni?

Usare le transazioni per descrivere la proprietà e i suoi passaggi mettendo in sicurezza il sistema al livello di ogni singola transazione sono passi fondamentali per raggiungere il nostro obiettivo. Come dicevamo in precedenza è la storia delle transazioni a dimostrare la ownership che vista la sua grande importanza deve essere salvata nel modo più sicuro possibile. Consideriamo la storia delle transazioni come una “gigantesca” struttura dati che poi altro non è che l’equivalente digitale di un libro mastro -ledger-.

Come possiamo garantire l’integrità di questi dati una volta che saranno distribuiti sulla rete?

Avere un solo ledger che “mantiene” tutta la storia delle transazioni potrebbe sembrare già un buon risultato e in effetti lo è…Ricordiamo comunque che il nostro obiettivo è quello di creare un sistema peer-to-peer di nodi su cui distribuire il ledger, trovandoci un ambiente di cui non possiamo fidarci a priori. Inoltre il controllo dei ledger passerà all’intera rete che non ha alcun punto di controllo o coordinamento.

Come facciamo a evitare che al ledger possano essere aggiunte o cancellate delle transazioni? In casi come questo il miglior modo per prevenire l’alterazione della storia è quello di renderlo non modificabile. Questo significa che il ledger e dunque la storia delle transazioni non può essere cambiata una volta scritta.

Dunque la sfida che ci poniamo è quella di rendere la blockchain non modificabile e nello stesso tempo in grado di accettare, aggiungere, nuove transazioni. Il risultato è una struttura dati che permette solo l’aggiunta in fondo, append, di nuove transazioni e che rende impossibile la modifica dei dati che sono stati aggiunti in passato.


Descrivere l’ownership: la prova venuta dal “passato”

L’obiettivo che ci poniamo in questo step è di descrivere/documentare l’ownership non solo allo scopo di dire che qualcuno è il proprietario di qualcosa, ma anche di fornire delle prove che supportino questa affermazione.

Anziché dunque descrivere lo stato corrente dell’ownership fornendo una lista di tutti i “possedimenti” degli owner sarebbe più utile al nostro scopo mantenere in un ledger la lista di tutte le transazioni che rappresentano i trasferimenti di proprietà in modo continuo. Ogni trasferimento di proprietà è documentato dai dati della transazione che definiscono puntualmente chi ha ceduto cosa e chi ha ricevuto in un determinato momento.

L’intera storia delle transazioni salvate in un ledger diventa dunque la storia di come ognuno ha ottenuto le “proprie proprietà”.

Come abbiamo visto essenzialmente ci sono due modi per descrivere l’ownership:

  • L’inventario (Inventory data)
  • Le transazioni (Transaction data)

L’inventario descrive solo lo stato attuale della proprietà: volendo fare un esempio è come se fosse la somma riportata sull’estratto conto bancario

Le transazioni descrivono invece tutti i trasferimenti di proprietà: riprendendo l’esempio precedente è come se fosse la lista movimenti di un conto bancario. E’ comunque possibile ricavare gli Inventory data aggregando i dati delle transazioni.

Entrambe descrivono l’ownership ma hanno un significato completamente diverso:

Gli Inventory data non fanno altro che rivendicare la proprietà mentre le Transaction data spiegano e in questo modo giustificano la proprietà.

Va da sé dunque che la scelta per la documentazione dell’ownership nella realizzazione della “nostra” blockchain ricadrà sulle transazioni per:

  • Descrivere i trasferimenti di proprietà
  • Mantenere la storia dei trasferimenti

Una transazione è l’atto di trasferire la proprietà da qualcuno verso qualcun’altro e si basa sui dati che contengono tutte le informazioni necessarie affinché ciò possa avvenire.

La informazioni usate dalla blockchain per descrivere una transazione sono di solito:

  • L’identificatore dell’account “mittente”
  • L’identificatore dell’account “destinatario”
  • La quantità o il bene che si vuol trasferire
  • Un identificatore temporale di quando la transazione deve avvenire
  • Una fee da pagare al sistema che esegue la transazione
  • Una prova che l’account mittente sia realmente d’accordo con la transazione

Sono più o meno le stesse informazioni necessarie durante un qualsiasi bonifico e/o giraconto bancario cui siamo abituati, ma nel caso della blockchain le analogie si fermano non appena si parla di fee.

Le banche e in generale qualsiasi ente o istituzione che faccia da mediatore “soffrono di centralizzazione” e in genere mantengono un “cartello” con la fee che ogni cliente deve o dovrà pagare per usufruire dei servizi.

La blockchain è un sistema distribuito senza un’autorità centrale e di conseguenza non ha un cartello delle fee da pagare. Usando la blockchain ogni utente dichiara al sistema quanto è intenzionato a spendere affinché la sua transazione venga eseguita.

La transazione fornisce dunque tutte le informazioni necessarie a eseguire il trasferimento di proprietà.

Eseguire una transazione significa far sì che tale trasferimento avvenga in linea con questi dati.

Eseguire una transazione significa aggiungere tale trasferimento al ledger.

Aggiungendo una transazione ad un ledger questa diventa a tutti gli effetti una parte della storia che sarà usata per documentare la proprietà.

La blockchain mantiene l’intera storia di tutte le transazioni avvenute conservando le loro informazioni all’interno della propria struttura dati esattamente nell’ordine in cui sono avvenute.

Mantenere l’ordinamento delle transazioni esattamente nello stesso identico modo in cui sono avvenute è fondamentale per far sì che ogni qual volta si voglia documentare l’ownership usando e aggregando i dati delle transazioni stesse il risultato non cambi.

Giunti a questo punto possiamo affermare che la storia delle transazioni è il cuore di ogni blockchain che gestisca la proprietà visto che proprio questa storia ci permette di documentarla. Va da sé che questo storico debba essere mantenuto completo, corretto e consistente per garantire l’integrità dell’intero sistema. La blockchain dovrà dunque adottare delle misure di sicurezza per garantire che solo le transazioni valide entrino a far parte del ledger.

Nell’esaminare la validità delle transazioni dobbiamo considerare tre aspetti:

  1. La correttezza formale
  2. La correttezza semantica
  3. Le autorizzazioni

Con correttezza formale si indica che la transazione debba contenere tutti i dati necessari e che questi siano nel formato corretto.

Stabilire la correttezza semantica di una transazione significa focalizzarsi sul significato dei dati e sugli effetti che produrranno. Per fare ciò è necessario avere una conoscenza del dominio trattato e verificare che la transazione rispetti le sue regole:

  • Assicurarsi che un account non invii più di quello che realmente possiede
  • Limitare il numero di asset che possono essere trasferiti in un’unica transazione
  • Limitare il numero di transazioni per utente
  • Limitare il numero di asset spendibili in un arco di tempo

Con autorizzazione si intende che solo l’account mittente può autorizzare la blockchain a eseguire una transazione a suo nome. Come risultato di ciò, la blockchain richiede che ogni transazione contenga delle informazioni che provino che effettivamente questa autorizzazione c’è stata.

Proteggere la proprietà con l’hashing: le impronte digitali dei dati

Le impronte digitali sono in grado di identificare univocamente le persone: un’impronta corrisponde ad una sola persona e viceversa.

Più o meno lo stesso concetto di identificazione potrebbe essere applicato ai dati delle transazioni usando gli hash value crittografici di cui la blockchain fa un uso intensivo.

Trovandoci in un sistema distribuito peer-to-peer probabilmente avremo a che fare con un grosso numeri di dati associati alle transazioni. E’ d’obbligo dunque cercare di identificare questi dati univocamente e riuscire a confrontarli nel modo più veloce possibile.

Le funzioni hash sono piccoli software che trasformano ogni tipo di dato in un numero di lunghezza fissa indipendentemente dalla dimensione dell’input. Accettano come input una porzione di dati per volta e creano un valore hash sulle base della struttura in bit del dato in ingresso. I valori hash possono anche avere degli zeri iniziali per raggiungere la lunghezza richiesta. Esistono diversi tipi di funzioni hash che si differenziano per la lunghezza del valore hash che generano. Un importante gruppo di queste funzioni sono le hash crittografiche che come dicevamo all’inizio del paragrafo creano le “impronte digitali” per i dati.

Le funzioni hash crittografiche hanno le seguenti proprietà:

  • Generano il valore hash per ogni tipo di dato velocemente
  • Sono deterministiche
  • Sono pseudorandomiche
  • Sono funzioni unidirezionali
  • Sono resistenti alle collisioni

La velocità in un sistema distribuito in grado di effettuare un grosso numero di transazioni è fondamentale per evitare che la lentezza di certi meccanismi generi disadozione.

Deterministica vuol dire che la funzione hash generare lo stesso valore hash ogni volta che è dato lo stesso input. Significa che se si dovesse notare una discrepanza di valori hash generati il perché va ricercato sull’input e non sui meccanismi interni della funzione.

Pseudorandomica indica che il valore calcolato da una funzione hash cambia in modo non prevedibile quando l’input cambia. Anche se il dato in ingresso variasse di un solo bit, il valore hash restituito potrebbe essere diversissimo.

Unidirezionale significa che dal valore hash calcolato — ndr output - non è possibile risalire in alcun modo all’input.

Resistente alle collisioni vuol dire che è altamente improbabile che dati due input distinti venga generato lo stesso valore hash. Possiamo dunque considerare il valore hash generato da una funzione hash come univoco per una certa porzione di dati in input e usare appunto questo valore per identificare i dati. In genere gli hash value generati non contengono solo i numeri da 0 a 9 ma anche le lettere da A a F che servono per rappresentare i numeri da 11 a 16. I numeri così fatti sono chiamati esadecimali.

Usando dunque le funzioni hash otteniamo per ogni porzione di input un valore hash crittografico univoco. E se volessimo fornire un unico valore hash per un gruppo di input indipendenti? Ricordiamo che le funzioni hash accettano un solo input alla volta: come fare allora in casi come quello della blockchain che si aspetta un unico valore hash per un gruppo di transazioni?

Niente paura, è possibile applicare uno di questi pattern:

  • Hashing indipendente
  • Hashing ripetuto
  • Hashing combinato
  • Hashing sequenziale
  • Hashing gerarchico

Con l’hashing indipendente applichiamo la funzione hash a ogni singola porzione di dato indipendentemente.

Con l’hashing ripetuto non facciamo altro che trasformare il valore hash generato come output nel successivo input della funzione hash.

L’obiettivo dell’hashing combinato è quello di generare un singolo valore hash per più porzioni di dato in una sola volta. Tale risultato si ottiene combinando le varie porzioni di dato in un unico pezzo, dandolo poi “in pasto” in un sol colpo alla funzione hash. Poiché combinare i dati ha un costo computazionale elevato in termini di potenza di calcolo, tempo e memoria tale metodo di hashing andrebbe usato solo quando i dati sono contenuti. Utilizzando tale pattern inoltre non abbiamo i valori hash per ogni singola porzione ma solo uno unico per la loro combinazione.

Usando l’hashing sequenziale si aggiorna in maniera incrementale il valore di hash via via che nuovi dati arrivano. E’ come usare una combinazione di hashing combinato e hashing incrementale: il valore di hash esistente è combinato con il nuovo dato in arrivo e passato come input alla funzione di hashing per calcolare il nuovo valore. Il suo vantaggio principale è che dato un arco temporale e il valore hash in quel dato momento è possibile riscotruirne la storia fino all’arrivo del nuovo dato.

L’idea che sta dietro all’hashing gerarchico è quella di usare l’hashing combinato solo su una coppia di valori creando così una “gerarchia” con un solo valore in cima esattamente come se fosse un albero. Procedendo così il risultato è un unico valore hash a fronte di una collezione di dati in ingresso. Rispetto ai metodi visti in precedenza risulta essere molto efficiente perché di volta in volta combina i valori hash che sono di lunghezza fissa invece che i dati originali che possono essere di qualsivoglia lunghezza. Inoltre combina solo due valori hash per volta -ndr a ogni passo- a differenza dell’hash combinato che elabora tutti i dati che gli sono forniti.

Qual è dunque lo scopo dei valori hash e come sono usati dalla blockchain?

  • Confrontare i dati

Primo fra tutti confrontare i dati senza confrontare il loro contenuto e soprattutto confrontare ogni tipo di dato in modo semplice come se stessimo confrontando appunto due numeri.

L’idea è appunto quella di confrontare i dati semplicemente confrontando i loro valori hash: se i valori hash differiscono i dati sono diversi altrimenti sono identici. Questo tipo di confronto funziona perché le funzioni hash sono resistenti alle collisioni.
  • Accorgersi se i dati sono cambiati

E’ una diretta conseguenza del confronto. Confrontare infatti il valore hash di alcuni dati che è stato calcolato “nel passato” con quello degli stessi dati “appena calcolato” è la chiave per identificare i cambiamenti. Immaginiamo di mandare delle informazioni a qualcuno. Se calcoliamo il valore hash di queste informazioni prima di inviarle e chi le riceve lo calcola appena ricevute si può facilmente verificare che: se sono identici le informazioni non sono state modificate durante il “percorso” altrimenti sono state in qualche modo alterate. Anche in questo caso ci si basa sulla proprietà di resistenza alle collisioni delle funzioni hash.

  • Creare dei riferimenti ai dati che siano sensibili al loro cambiamento

Lo scopo è quello di rifersi ai dati, nel caso della blockchain ai transaction data, che sono conservati da qualche parte, un hard disk o un database, ed essere che siano rimasti immutati. L’idea è quella di combianare gli hash crittografici dei dati con le informazioni del “posto” dove sono stati conservati. Se i dati sono cambiati, entrambe le informazioni non saranno più consistenti e di conseguenza il valore hash di riferimento -hash reference- non sarà più valido.

Le hash references sono dunque un riferimento al dato con lo scopo di verificare che effetivamente tali dati cui si riferiscono non siano stati modificati dopo essere stati creati. Nel caso cui lo fossero stati le reference non permette più il loro recupero. In questi casi si parla di hash references “rotti” o “non validi”.

Lo scopo finale delle hash references è di evitare per l’utente possa “riprendere” dei dati che sono stati modificati per errore tecnico o per dolo.

  • Salvare i dati in un modo che sia sensibile al loro cambiamento

L’obiettivo è quello di “conservare” grosse quantità di dati relativi alle transazioni che supponiamo non debbano essere modificati. Ogni modifica in tal senso deve essere riconosciuta in modo veloce e semplice.

Sono due i pattern che in genere sono usati per realizzare una struttura dati che abbia queste caratteristiche:

  • La catena (chain)
  • L’albero (tree)

Con chain si intende una catena di dati collegati, conosciuta anche come linked list, che è costituita da insieme di dati che contengono anche un hash reference a un altro gruppo di dati. Una struttura del genere è molto utile quando i dati da contenervi non sono subito disponibili tutti ma arrivano passo dopo passo in modo continuativo.

L’ultima hash reference sarà utilizzata per accedere a tutti i dati della chain in ordine inverso rispetto a quello del loro arrivo. Tale reference è anche detta “head of the chain” visto che si riferisce al più recente insieme di dati che sono stati inseriti, head appunto.
Linked List con R3 “head of the chain”

Con la struttura ad albero, ricordate l’hashing gerarchico?, si intende rappresentare più insiemi distinti di dati tutti disponibili in un dato momento e accessibili attraverso una unica hash reference. Una così fatta struttura è anche chiamata Merkle tree ed è percorribile dall’alto verso il basso accedendo a partire dall’hash reference “più alta” (root).

Merkle tree con R root

Proteggere e identificare gli account degli utenti con la crittografia

Oltre alle funzioni hash, la blockchain usa in modo intensivo anche un’altra tecnologia: la crittografia asimmetrica. E’ alla base dell’identificazione degli utenti sulla blockchain e della protezione della loro proprietà. Ha lo scopo di identificare univocamente gli owner e le loro proprietà assicurando che solo gli autorizzati vi possano accedere.

La caratteristica principale della proprietà privata è la sua esclusività. Il diritto di trasferire la proprietà da un account a un altro è consentito solo all’owner dell’account che effettivamente possiede l’onwership. Da qui lo scopo della blockchain di proteggere la proprietà assegnata a un account senza operare delle restrizioni alla natura distribuita del sistema.

L’idea che sta dietro alla crittografia può essere capita in modo semplice paragonando gli account degli utenti sulla blockchain alle cassette della posta: il loro indirizzo è conosciuto a tutti, chiunque può inserirvi delle lettere ma solo il proprietario può aprirle usando la chiave. Questa dualità ha un corrispettivo nel mondo digitale: la cifratura a chiave pubblica-privata.

La chiave pubblica è utilizzata per identificare l’account a chiunque voglia effettuare dei trasferimenti di proprietà.
L’accesso è invece ristretto solo e soltanto a chi possiede la corrispondente chiave privata.

Esistono essenzialmente due tecniche di crittografia:

  • Simmetrica
  • Asimmetrica

La simmetrica utilizza la stessa chiave sia per cifrare che per decifrare i dati: né consegue che chiunque sia in grado di cifrare i dati con una chiave è in grado di decrifarli con la stessa chiave.

La crittografia asimmetrica usa invece due chiavi complementari: un testo cifrato con una chiave può essere decifrato solo usando l’altra chiave e viceversa.

Il vantaggio di questa tecnica crittografica è dunque che non è possibile decifrare dei dati con la stessa chiave con cui sono stati cifrati. Per fare tutte e due le operazioni dobbiamo dunque possedere entrambe le chiavi.

In questo modo “giocando” sulla distribuzione delle chiavi possiamo distinguere i gruppi di persone che possono cifrare i dati da quelli che possono decifrarli.

Quando si utilizza la crittografia nel mondo reale, queste chiavi sono in genere chiamate chiave pubblica e chiave privata in base al ruolo che si attribuisce loro. La chiave pubblica è condivisa con chiunque mentre la chiave privata è tenuta segreta. Proprio per questo motivo la crittografia asimmetrica è anche detta crittografia a chiave pubblica e privata.

I casi d’uso principali delle chiavi pubbliche e chiavi private sono in genere due:

  • Chiunque può usare la chiave pubblica per cifrare i dati che possono essere decifrati solo da chi possiede la chiave privata (public to private). Questo è il modo di mandare messaggi sicuri al proprietario della chiave privata.
  • Il proprietario della chiave privata la usa per cifrare i dati che possono essere decifrati da tutti quelli che possiedono la chiave pubblica corrispondente (private to public). Questo è il modo di essere certi di chi sia l’autore del messaggio.

La blockchain usa:

  • l’approccio public to private per identificare gli account degli utenti e consentire il trasferimento di proprietà tra loro. I numeri che identificano l’account -address- costituiscono la parte pubblica delle chiavi asimmetriche.
  • l’approccio private to public per autorizzare le transazioni. L’owner dell’account che “vuol cedere” la proprietà cifra i dati, o una loro parte, usando la sua chiave privata. Questi dati possono essere verificati da chi possiede la chiave pubblica che non è altro che l’indirizzo dell’account che sta inviando, autorizzando, la transazione.

Riassumendo dunque, sappiamo che la blockchain usa la chiave pubblica come indirizzo degli account e l’approccio public to private per trasferire la proprietà tra gli stessi account. D’altra parte necessita di un meccanismo che assicuri che il proprietario dell’account voglia realmente trasferire la proprietà: la vera e propria autorizzazione al trasferimento che usa la parte private to public dell’approccio asimmetrico.

L’autorizzazione al trasferimento in seno alla blockchain assume la forma di una vera e propria firma digitale che deve soddisfare i seguenti requisiti:

  • Certificare in modo assoluto la volontà di chi esegue il trasferimento
  • Essere unica per ogni data transazione in modo tale che non possa essere utilizzata per autorizzare eventuali altre transazioni all’insaputa dell’owner dell’account
  • Può essere creata solo dall’owner dell’accout che vuole autorizzare
  • Deve essere facile da verificare per chiunque

Tale firma digitale è usata nella blockchain per:

  • Firmare le transazioni
  • Verificare le transazioni

Firmare una transazione

Per creare una firma digitale per una transazione, l’owner dell’account che vuole effettuare un trasferimento di proprietà, dovrà:

  1. Costruire la transazione con tutte le informazioni necessarie (indirizzo dell’account, cosa trasferire, ecc…) eccetto per adesso la firma digitale
  2. Creare l’hash value dei dati della transazione
  3. Cifrare il valore hash con la propria chiave privata
  4. Aggiungere il testo cifrato, cypher text, creato in precedenza, alla transazione per costituirne la firma digitale

Verificare una transazione

Per verificare una transazione dovranno essere effettuati i seguenti passi:

  1. Creare il valore hash dei dati della transazione escludendo la firma digitale
  2. Decifrare la firma digitale della transazione con la chiave pubblica e cioé usando l’indirizzo dell’account mittente
  3. Confrontare il valore hash ottenuto al punto 1 con quello ottenuto al punto 2: se sono identici allora la transazione è autorizzata dall’owner della chiave privata e cioé dell’account mittente, altrimenti no.

La firma digitale di una transazione è dunque la combinazione di:

  • L’hash value generato per i dati della transazione
  • Il testo cifrato che può essere ricondotto alla corrispondente chiave privata dell’account

Considerando il fatto che i valori hash possono essere considerati alla stregua delle impronte digitali, essi sono unici per ogni transazione.

La proprietà principale della crittografia asimmetrica è che i dati cifrati con una chiave possono essere decifrati solo con la chiave corrispondente: l’associazione di queste chiavi è unica. Una corretta decifratura con la chiave pubblica dei dati cifrati è una prova schiacciante della corrispondenza della chiave privata.

La combinazione di questi due concetti è usata per creare dei dati cifrati che possono essere ricondotti a una unica transazione e a una specifica chiave privata. In questo modo la firma digitale è adatta a provare che l’owner della chiave privata che è stata usata per generare tale firma è “d’accordo” con il contenuto della transazione.

Block#6 delivered.

Block#5 link

Credits: 
- Blockchain Basics di Daniel Drescher