Primitive e interfacce

Poi definiamo (informalmente, ma poi formalmente) due parti importanti del linguaggio utilizzato in Tink, Primitive e Interface.

originario

Una primitiva è un oggetto matematico che corrisponde a tutti gli algoritmi che eseguono alcune attività in modo sicuro. Ad esempio, la primitiva AEAD è costituita da tutti gli algoritmi di crittografia che soddisfano le proprietà di sicurezza richieste da Tink a un Aead.

Sottolineiamo che le primitive non sono legate a un linguaggio di programmazione o a un modo specifico per accedervi. Invece, si dovrebbe pensare alla primitiva come a un oggetto puramente matematico. Ad esempio, se consideriamo l'AEAD, fondamentalmente sarà costituito da coppie di funzioni, una che esegue la crittografia e una che esegue la decrittografia.

Interfacce

Un'interfaccia è un modo in cui forniamo agli utenti l'accesso a una primitiva. Ad esempio, ci aspettiamo che in futuro Tink fornirà un'interfaccia Mac, ma anche un'interfaccia StreamingMac, che consentirà di calcolare il Mac di dati che non vengono caricati direttamente in memoria.

Tieni presente che qui distinguiamo esplicitamente le interfacce e le primitive. Ciò dovrebbe chiarire che gli oggetti matematici a cui queste due interfacce danno accesso sono gli stessi.

Definizioni formali

Per la maggior parte dei lettori, probabilmente le spiegazioni intuitive sopra riportate sono sufficienti. Ciononostante, riteniamo che a volte possa essere importante fornire definizioni formali di questi concetti.

Funzioni crittografiche

Il concetto di funzione crittografica non è importante quanto il concetto di primitiva, ma dobbiamo introdurlo per definire formalmente i primitivi.

Funzione crittografica

Una funzione crittografica è una mappa

\[ f: {\bf K} \times {\bf R} \times {\bf I} \to {\bf O}\]

da un set \({\bf K}\) (spazio delle chiavi), un insieme \({\bf R} = \{0,1\}^{\infty}\) (casuale, che supponiamo sia l'insieme di stringhe di bit infinite) e un set \({\bf I}\) (spazio di input), a un set \({\bf O}\) (spazio di output).

Più avanti sarà chiaro perché abbiamo aggiunto un parametro specifico di casualità.

Ad esempio, mostriamo una possibilità di come questi concetti possano essere mappati ad AES-GCM. Per ogni dimensione valida della chiave \(s_k\), dimensione del nonce \(s_n\)e dimensione del tag \(s_t\), AES-GCM è costituito da due funzioni crittografiche, una per la crittografia e una per la decrittografia. Entrambi avranno lo stesso spazio per le chiavi \({\bf K} = \{0,1\}^{s_k}\).

Per la funzione di crittografia \(\mathrm{Enc}\), verranno utilizzati i primi \(s_n\) bit di casualità per selezionare il nonce.

Consenti \({\bf B} = \{0,1\}^8\) di indicare un byte. Lo spazio di input della funzione di crittografia è costituito da coppie \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) di coppie di stringhe di byte di lunghezza arbitraria. Il primo elemento della coppia deve essere il messaggio, il secondo i dati associati. Lo standard AES-GCM ha un limite superiore alla lunghezza degli input, ma preferiamo consentire lunghezze arbitrarie e invece aggiungere un simbolo di errore speciale \(\bot\) allo spazio di output. Lo spazio di output diventa quindi \({\bf O} = {\bf B}^* \cup \{\bot\}\), dove definiamo arbitrariamente il risultato dei calcoli riusciti come la concatenazione \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) come indicato nello standard e l'output\(\bot\), nel caso in cui alcuni input siano troppo lunghi. Quindi, per una chiave fissa, la funzione di crittografia diventa di tipo \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^* \times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\).

Per la funzione di decrittografia \(\mathrm{Dec}\) lo spazio delle chiavi è lo stesso. Per coincidenza, lo spazio di input è lo stesso: \({\bf I} ={\bf B}^* \times {\bf B}^*\), ma ora il primo elemento deve essere l'output della funzione di crittografia, mentre il secondo è ancora i dati associati.

Anche lo spazio di output è lo stesso \({\bf O} = {\bf B}^* \cup \{\bot\}\) (sempre per caso). L'interpretazione è leggermente diversa, poiché \(\bot\) di solito indica un errore di autenticazione (sebbene sarà anche l'output nel caso in cui un input sia troppo lungo).

Sottolineiamo che la formalizzazione descritta sopra non è l'unica opzione per formalizzare lo standard. Ad esempio, si potrebbe considerare il nonce come parte dell'input, invece di leggerlo dalla casualità (il che determina una primitiva molto diversa). In alternativa, si potrebbe definire l'output come un triplo contenente il nonce, il testo crittografato e il tag (anziché la concatenazione). Oppure si potrebbe limitare lo spazio delle chiavi (in modo arbitrario) a\({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\).

Algoritmo crittografico:

Un algoritmo crittografico (simmetrico) è una tupla

\[(f_1, ... f_k)\]

di funzioni crittografiche, dove tutte le funzioni hanno lo stesso spazio per le chiavi. Il tipo dell'algoritmo crittografico è la tupla \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\).

Ad esempio, per ogni tripla \((s_k, s_n, s_t)\) di dimensione di chiave, nonce e tag valida, AES-GCM\({}_{s_k, s_n, s_t}\) è un algoritmo crittografico con le due funzioni \(\mathrm{Enc}\) descritte \(\mathrm{Dec}\) in precedenza.

Primitivi e interfacce

Definiamo poi una primitiva crittografica.

originario
Un primitivo è un insieme di algoritmi crittografici in cui tutti gli algoritmi hanno lo stesso tipo \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\)e gli spazi chiave degli algoritmi sono disgiunti a coppie.

Prendiamo ad esempio la \(\mathrm{AEAD}\) primitiva di Tink. Ha diversi algoritmi, tra cui AES-GCM per chiavi di dimensioni 128 e 256 bit, con dimensione nonce a 96 bit, AES-EAX con alcune dimensioni di chiave e XChaCha20Poly1305. Hanno spazi chiave separati, ma offrono tutte le stesse funzioni crittografiche\(\mathrm{Enc}\) e \(\mathrm{Dec}\). (In questa discussione formale non vediamo lo scopo di comprimere le diverse dimensioni delle chiavi di AES-GCM, ma ovviamente si potrebbe farlo).

Definizione delle primitive

In genere si pensa alle primitive nel definire innanzitutto le proprietà delle funzioni crittografiche, quindi semplicemente considerare le primitive come tutti questi algoritmi.

Ad esempio, per l'AEAD diremmo che \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) è "sempre" soddisfatto (tranne se il testo non crittografato \(m\) è troppo lungo). Inoltre, disponiamo di proprietà di sicurezza; ad esempio, per una chiave casuale, la crittografia è semanticamente sicura.

La primitiva AEAD corrisponde quindi all'insieme di tutti gli algoritmi crittografici che soddisfano queste proprietà. In altre parole, in pratica, quando definiamo una primitiva specifica, la definiamo in base alle proprietà. Non forniamo un elenco di algoritmi, come suggerisce la definizione.

Interfacce

Un'interfaccia in Tink dà accesso a una primitiva, nel senso che consente di calcolare un elemento dello spazio di output dallo spazio di input. Ad esempio, considera l'interfaccia AEAD in Java:

public interface Aead {
  byte[] encrypt(byte[] plaintext, byte[] associated_data) throws GeneralSecurityException;
  byte[] decrypt(byte[] ciphertext, byte[] associated_data) throws GeneralSecurityException;
}

Tieni presente che non concediamo accesso alla casualità. Consentiamo invece all'utente di fornire elementi dello spazio di input. Non consentire l'accesso alla casualità è ovviamente di proposito.1

Tink a volte offre più interfacce per una singola primitiva. Questa opzione può essere molto utile, poiché a volte i requisiti sono diversi. Tuttavia, questo ha un costo: in generale, maggiore è il numero di interfacce offerte, minore è l'interoperabilità. Ad esempio, immagina che qualcuno scriva una libreria basata su Tink che richiede all'utente di passare un oggetto Aead (per criptare qualcosa internamente). Se Tink offre troppe interfacce diverse per la \(\mathrm{AEAD}\) primitiva, è probabile che l'utente non abbia un'istanza pronta che funzioni per la chiave scelta dall'utente e contemporaneamente per la libreria. Di conseguenza, l'aggiunta di altre interfacce è un compromesso.


  1. Le crittografie AEAD hanno la proprietà di essere protette dagli attacchi di testo crittografato scelti, il che è garantito solo se non viene riutilizzato il nonce. L'interfaccia Aead in Tink è progettata in modo da impedire il riutilizzo dei nonce: l'utente non può fornire un nonce come input per la crittografia, mentre un nuovo nonce viene generato casualmente per ogni operazione di crittografia.