Ora definiamo (in forma informale, ma poi più formalmente) due parti importanti il linguaggio usato in Tink, Primitive e Interface.
originario
Un oggetto primitivo è un oggetto matematico che corrisponde a tutti gli algoritmi di eseguire delle attività in modo sicuro. Ad esempio, la primitiva AEAD è composta da tutti Algoritmi di crittografia che soddisfano le proprietà di sicurezza richieste da Tink di un Aead.
Sottolineiamo che le primitive non sono legate a un linguaggio di programmazione o a una modo di per l'accesso. Si dovrebbe invece considerare il primitivo come matematico. Ad esempio, se consideriamo l'AEAD, fondamentalmente è composta 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 un primitivo.
Ad esempio, prevediamo che in futuro Tink fornirà un'interfaccia Mac
,
ma anche un'interfaccia StreamingMac
, che consente di calcolare MAC di
che non vengono caricati direttamente in memoria.
Tieni presente che in questo caso distinguiamo esplicitamente interfacce e primitive. Questa operazione dovrebbe chiariscono che l'oggetto matematico a cui queste due interfacce forniscono sono le stesse.
Definizioni formali
Per la maggior parte dei lettori le spiegazioni intuitive sopra citate sono probabilmente sufficienti. Ciononostante, riteniamo che talvolta possa essere importante fornire servizi definizioni di questi concetti.
Funzioni crittografiche
Il concetto di funzione crittografica non è tanto importante quanto quello di una ma dobbiamo introdurlo per definirla formalmente.
- Funzione crittografica
Una funzione crittografica è una mappa
\[ f: {\bf K} \times {\bf R} \times {\bf I} \to {\bf O}\]
da un insieme \({\bf K}\) (lo spazio chiave), un insieme \({\bf R} = \{0,1\}^{\infty}\) (casualità, che consideriamo l'insieme di bitstring infiniti), e un imposta \({\bf I}\) (spazio di input) su un insieme \({\bf O}\) (spazio di output).
In seguito sarà chiaro perché abbiamo aggiunto un parametro di casualità specifico.
Ad esempio, mostriamo una possibilità di come questi concetti possano essere AES-GCM. Per ogni dimensione della chiave, \(s_k\)nonce \(s_n\)e dimensione del tag valide \(s_t\), AES-GCM consiste di due funzioni crittografiche, una per per la crittografia e uno per la decrittografia. Entrambi avranno lo stesso spazio per le chiavi \({\bf K} = \{0,1\}^{s_k}\).
Per la funzione di crittografia \(\mathrm{Enc}\), i primi \(s_n\) bit di la casualità sarà utilizzata per selezionare il nonce.
Indica \({\bf B} = \{0,1\}^8\) un byte. Lo spazio di input della funzione di crittografia è costituito dalle coppie \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) di coppie di stringhe di byte di lunghezza arbitraria. Il primo elemento della coppia è destinato a essere il messaggio, il secondo è i dati associati. Lo standard AES-GCM ha un limite massimo per la lunghezza gli input, ma preferiamo consentire lunghezze arbitrarie e aggiungere invece uno speciale simbolo di errore \(\bot\) nello spazio di output. Lo spazio di output diventa quindi \({\bf O} = {\bf B}^* \cup \{\bot\}\), dove definiamo arbitrariamente il risultato eseguiti in modo corretto come concatenazione \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) come specificato nello standard \(\bot\), nel caso in cui un input sia troppo lungo. Quindi, per una chiave fissa, 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 della chiave è lo stesso. La spazio di input per caso è lo stesso: \({\bf I} ={\bf B}^* \times {\bf B}^*\), ma ora il primo elemento deve essere l'output della funzione di crittografia, mentre la seconda sono i dati associati.
Anche lo spazio di output è lo stesso \({\bf O} = {\bf B}^* \cup \{\bot\}\) (sempre una coincidenza). L'interpretazione è un po' diversa, perché \(\bot\) di solito indica un errore di autenticazione (anche se sarà anche nel caso in cui qualche input sia troppo lungo).
Sottolineiamo che la formalizzazione di cui sopra non è l'unica opzione per formalizzare standard. Ad esempio, si potrebbe considerare il nonce come parte dell'input, di leggerlo dalla casualità (che si traduce in una primitiva molto diversa). In alternativa, si potrebbe definire l'output come una tripla contenente il nonce, il testo crittografato e il tag (anziché la concatenazione). Oppure si potrebbe limita lo spazio della chiave (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, in cui tutte le funzioni hanno lo stesso spazio di chiavi. La Il tipo di algoritmo crittografico è la tupla \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\).
Ad esempio, per ogni tripla valida \((s_k, s_n, s_t)\) di chiave, nonce e tag AES-GCM\({}_{s_k, s_n, s_t}\) è un algoritmo crittografico con due funzioni \(\mathrm{Enc}\) e \(\mathrm{Dec}\) descritte sopra.
Primitive e interfacce
Ora definiamo 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.
Considera ad esempio il \(\mathrm{AEAD}\) primitivo in Tink. Ha diverse algoritmi, tra cui AES-GCM per le dimensioni delle chiavi a 128 e 256 bit, con 96 bit, AES-EAX con alcune dimensioni delle chiavi e XChaCha20Poly1305. Hanno spazi delle chiavi disgiunti, ma tutte forniscono le stesse funzioni crittografiche \(\mathrm{Enc}\) e \(\mathrm{Dec}\). (In qualche modo non vediamo uno scopo comprimendo diverse dimensioni chiave di AES-GCM in questa discussione formale, ma di ovviamente si può fare).
Definizione delle primitive
Il solito modo di pensare alle primitive è definire anzitutto le proprietà dei funzioni crittografiche, per poi considerare semplicemente la primitiva tali algoritmi.
Ad esempio, per AEAD diremmo che \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) è "sempre" soddisfatto (tranne se, ad esempio, se il testo non crittografato \(m\) è troppo lunga). Inoltre, disponiamo di proprietà per la sicurezza; ad esempio per chiave casuale, la crittografia è semantialmente sicura.
La primitiva AEAD è quindi semplicemente l'insieme di tutti gli algoritmi crittografici che soddisfano queste proprietà. In altre parole, in pratica, quando definiamo una specifica primitive, li definiamo in base alle proprietà. Non forniamo un elenco algoritmi, come suggerisce la definizione.
Interfacce
Un'interfaccia in Tink dà accesso a una primitiva, nel senso che permette per calcolare un elemento dello spazio di output a partire 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 viene concesso l'accesso alla casualità. Consentiamo invece all'utente di forniscono elementi dello spazio di input. Il divieto di accesso alla casualità è impostato su corso di proposito.1
A volte Tink offre più interfacce per una singola primitiva.
Questo può essere molto utile, dato che i requisiti a volte differiscono. Operazione in corso
ha un prezzo: in generale, più interfacce viene offerta, minore è
e l'interoperabilità. Ad esempio, immagina
che qualcuno scrive una libreria basata su Tink che richiede all'utente di passare
Oggetto Aead
(per criptare qualcosa internamente). Se Tink ne offre troppe
diverse interfacce al \(\mathrm{AEAD}\) primitivo, le probabilità sono elevate
che l'utente
non dispone di un'istanza pronta che funziona per la chiave scelta dall'utente
contemporaneamente. Pertanto, aggiungere altre interfacce è un compromesso.
-
Le crittografie AEAD hanno la proprietà di essere sicure contro determinati attacchi di testo crittografato, il che è garantito solo se non il riutilizzo del nonce. L'interfaccia di Aead in Tink è progettata in modo da impedisce il riutilizzo del nonce: l'utente non può fornire un nonce come input per la crittografia, viene generato in modo casuale un nuovo nonce per ogni operazione di crittografia. ↩