Primitive und Schnittstellen

Als Nächstes definieren wir (informelle, aber dann formeller) zwei wichtige Teile der in Tink verwendeten Sprache, den Primitive und die Interface.

Primitiv

Ein Primitive ist ein mathematisches Objekt, das allen Algorithmen entspricht, die eine Aufgabe auf sichere Weise ausführen. Die AEAD-Primitive besteht beispielsweise aus allen Verschlüsselungsalgorithmen, die die Sicherheitseigenschaften erfüllen, die Tink für einen Aead-Wert benötigt.

Wir betonen, dass Primitive nicht an eine Programmiersprache oder eine bestimmte Art des Zugriffs gebunden sind. Stattdessen sollte man sich das Primitive als rein mathematisches Objekt vorstellen. Wenn wir beispielsweise AEAD in Betracht ziehen, besteht diese im Wesentlichen aus Funktionspaaren, von denen eines die Verschlüsselung und eines für die Entschlüsselung durchführt.

Interfaces

Über eine Schnittstelle gewähren wir Nutzern Zugriff auf eine Primitive. Wir gehen beispielsweise davon aus, dass Tink in Zukunft eine Mac-Schnittstelle, aber auch eine StreamingMac-Schnittstelle bereitstellen wird, mit der der MAC von Daten berechnet werden kann, die nicht direkt in den Arbeitsspeicher geladen werden.

Beachten Sie, dass hier ausdrücklich zwischen Schnittstellen und Primitiven unterschieden wird. Daraus sollte ersichtlich sein, dass das mathematische Objekt, auf das diese beiden Schnittstellen Zugriff gewähren, dasselbe ist.

Formale Definitionen

Für die meisten Leser reichen die oben genannten intuitiven Erklärungen wahrscheinlich aus. Dennoch sind wir der Meinung, dass es manchmal wichtig sein kann, diese Konzepte formal zu definieren.

Kryptografische Funktionen

Das Konzept einer kryptografischen Funktion ist nicht so wichtig wie das Konzept einer Primitive, aber wir müssen es einführen, um die formale Definition von Primitiven zu definieren.

Kryptografische Funktion

Eine kryptografische Funktion ist eine Zuordnung.

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

aus einem Set \({\bf K}\) (Schlüsselbereich), einer Menge \({\bf R} = \{0,1\}^{\infty}\)(Zufälligkeit, die der Satz unendlicher Bitstrings ist) und einer Menge \({\bf I}\) (Eingaberaum) zu einer Menge \({\bf O}\) (Ausgabebereich).

Es wird später klar, warum wir einen bestimmten Parameter für die Zufälligkeit hinzugefügt haben.

Als Beispiel zeigen wir eine Möglichkeit, wie diese Konzepte AES-GCM zugeordnet werden können. Für jede gültige Schlüsselgröße \(s_k\), Nonce-Größe \(s_n\)und Tag-Größe\(s_t\)besteht AES-GCM aus zwei kryptografischen Funktionen, einer für die Verschlüsselung und einer für die Entschlüsselung. Beide haben denselben Schlüsselbereich \({\bf K} = \{0,1\}^{s_k}\).

Für die Verschlüsselungsfunktion \(\mathrm{Enc}\)werden die ersten \(s_n\) Bits der Zufälligkeit verwendet, um die Nonce auszuwählen.

Geben Sie \({\bf B} = \{0,1\}^8\) ein Byte an. Der Eingabebereich der Verschlüsselungsfunktion besteht aus den Paaren \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) von Bytestrings beliebiger Länge. Das erste Element des Paars ist die Nachricht, das zweite Element die zugehörigen Daten. Der AES-GCM-Standard hat eine Obergrenze für die Länge der Eingaben, aber wir bevorzugen beliebige Längen und fügen stattdessen ein spezielles Fehlersymbol \(\bot\) in den Ausgabebereich ein. Der Ausgaberaum wird dann zu \({\bf O} = {\bf B}^* \cup \{\bot\}\), wobei wir das Ergebnis erfolgreicher Berechnungen willkürlich als Verkettung \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) definieren, wie im Standard angegeben, und Ausgabe\(\bot\), falls eine Eingabe zu lang ist. Daher erhält die Verschlüsselungsfunktion für einen festen Schlüssel den Typ \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^* \times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\).

Für die Entschlüsselungsfunktion \(\mathrm{Dec}\) ist der Schlüsselbereich derselbe. Der Eingabebereich ist zufälligerweise derselbe: \({\bf I} ={\bf B}^* \times {\bf B}^*\), aber das erste Element ist jetzt die Ausgabe der Verschlüsselungsfunktion, während das zweite Element immer noch die verknüpften Daten ist.

Der Ausgabebereich ist auch zufällig \({\bf O} = {\bf B}^* \cup \{\bot\}\) gleich. Die Interpretation unterscheidet sich geringfügig, da \(\bot\) normalerweise einen Authentifizierungsfehler angibt (es wird aber auch ausgegeben, wenn eine Eingabe zu lang ist).

Wir betonen, dass die obige Formalisierung nicht die einzige Möglichkeit ist, den Standard zu formalisieren. Beispielsweise könnte die Nonce als Teil der Eingabe betrachtet werden, anstatt sie aus der Zufälligkeit zu lesen (was zu einer ganz anderen Primitive führt). Alternativ können Sie die Ausgabe als Magisches Dreieck definieren, das die Nonce, den Geheimtext und das Tag (anstelle der Verkettung) enthält. Alternativ kann der Schlüsselbereich (eher willkürlich) auf\({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\)beschränkt werden.

Kryptografischer Algorithmus:

Ein (symmetrischer) kryptografischer Algorithmus ist ein Tupel

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

von kryptografischen Funktionen, bei denen alle Funktionen denselben Schlüsselraum haben. Der Typ des kryptografischen Algorithmus ist das Tupel \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\).

Beispielsweise ist AES-GCM\({}_{s_k, s_n, s_t}\) für jedes gültige 3-fache \((s_k, s_n, s_t)\) von Schlüssel, Nonce und Tag-Größe ein kryptografischer Algorithmus mit den beiden Funktionen \(\mathrm{Enc}\) und \(\mathrm{Dec}\) wie oben beschrieben.

Primitive und Schnittstellen

Als Nächstes definieren wir eine kryptografische Primitive.

Primitiv
Ein Primitive ist ein Satz kryptografischer Algorithmen, wobei alle Algorithmen den gleichen Typ haben \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\)und die Schlüsselbereiche der Algorithmen paarweise disjunkt sind.

Betrachten Sie als Beispiel das \(\mathrm{AEAD}\) Primitive in Tink. Es verfügt über mehrere Algorithmen, darunter AES-GCM für Schlüsselgrößen 128 und 256 Bit mit Nonce-Größe 96 Bit, AES-EAX mit einigen Schlüsselgrößen und XChaChaCha20Poly1305. Sie haben disjunkte Schlüsselbereiche, bieten aber die gleichen kryptografischen Funktionen\(\mathrm{Enc}\) und \(\mathrm{Dec}\). Wir sehen in dieser formellen Diskussion keinen Zweck in der Minimierung verschiedener Schlüsselgrößen von AES-GCM, aber natürlich könnte dies der Fall sein.

Primitive definieren

In der Regel definieren Sie Primitiven zuerst die Eigenschaften der kryptografischen Funktionen und betrachten dann einfach die Primitive als alle entsprechenden Algorithmen.

Für AEAD würden wir beispielsweise sagen, dass \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) immer erfüllt ist (außer wenn der Klartext \(m\) zu lang ist). Darüber hinaus gibt es noch Sicherheitseigenschaften. Bei einem zufälligen Schlüssel ist die Verschlüsselung beispielsweise semantisch sicher.

Das AEAD-Primitive ist dann einfach der Satz aller kryptografischen Algorithmen, die diese Eigenschaften erfüllen. Mit anderen Worten: Wenn wir ein bestimmtes Primitive definieren, definieren wir es in der Praxis anhand von Attributen. Wir geben keine Liste mit Algorithmen, wie die Definition schon andeutet.

Interfaces

Eine Schnittstelle in Tink bietet Zugriff auf eine Primitive in dem Sinne, dass sie es ermöglicht, ein Element des Ausgabebereichs aus dem Eingabebereich zu berechnen. Betrachten Sie beispielsweise die AEAD-Schnittstelle in Java:

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

Beachten Sie, dass wir keinen Zugriff auf die Zufälligkeit gewähren. Stattdessen können Nutzende Elemente des Eingabebereichs zur Verfügung stellen. Sie sollten den Zugriff auf die Zufälligkeit natürlich absichtlich verhindern.1

Tink bietet manchmal mehrere Schnittstellen für eine einzelne Primitive an. Das kann sehr nützlich sein, da die Anforderungen manchmal variieren. Dies hat jedoch seinen Preis: Im Allgemeinen gilt: Je mehr Schnittstellen die Interoperabilität hat, desto geringer ist die Interoperabilität. Stellen Sie sich beispielsweise vor, dass jemand eine auf Tink basierende Bibliothek schreibt, für die der Nutzer ein Aead-Objekt übergeben muss (um etwas intern zu verschlüsseln). Wenn Tink zu viele verschiedene Schnittstellen zum \(\mathrm{AEAD}\) -Primitive anbietet, ist die Wahrscheinlichkeit hoch, dass der Nutzer nicht über eine Instanz verfügt, die gleichzeitig für den vom Nutzer ausgewählten Schlüssel und die Bibliothek funktioniert. Daher ist das Hinzufügen weiterer Schnittstellen ein Kompromiss.


  1. AEAD-Chiffren haben die Eigenschaft, dass sie vor ausgewählten Geheimtextangriffen sicher sind. Dies ist nur dann garantiert, wenn die Nonce nicht wiederverwendet wird. Die Aead-Schnittstelle in Tink ist so konzipiert, dass sie die Wiederverwendung von Nonces verhindert: Der Nutzer kann keine Nonce als Eingabe für die Verschlüsselung bereitstellen. Stattdessen wird für jeden Verschlüsselungsvorgang nach dem Zufallsprinzip eine neue Nonce generiert.