Symmetrische Verschlüsselung

Symmetrische Verschlüsselung verwendet denselben Schlüssel zum Ver- und Entschlüsseln. Die wichtigste Idee auf dieser Seite ist der Unterschied zwischen einer einfachen Chiffre und einer AEAD-Chiffre (Authenticated Encryption with Associated Data). AES-CBC oder AES-CTR allein bieten Vertraulichkeit und sonst nichts: Ein Angreifer, der den Klartext nicht lesen kann, kann trotzdem Bits im Chiffretext umkehren, und der Empfänger wird das manipulierte Ergebnis entschlüsseln, ohne jeden Hinweis darauf, dass etwas nicht stimmt. AES-GCM und ChaCha20-Poly1305 authentifizieren ebenso wie sie verschlüsseln, sodass Manipulation erkannt wird und die Entschlüsselung eindeutig fehlschlägt. Bevorzugen Sie eine AEAD-Chiffre, sofern nicht etwas Spezifisches einen einfachen Modus erzwingt.

 

Alle Funktionen befinden sich in sgcCrypto_AES, sgcCrypto_Modes, sgcCrypto_ChaCha und sgcCrypto_Poly1305. Verwenden Sie niemals einen IV oder eine Nonce erneut mit demselben Schlüssel: Bei CTR und den AEAD-Chiffren schwächt eine Wiederverwendung nicht nur die Vertraulichkeit, bei GCM speziell bricht sie die Authentifizierungsgarantie vollständig. Erzeugen Sie für jede Nachricht einen neuen mit sgcRandomBytes.

 

AES-GCM (empfohlen)

 

Der AEAD-Modus, den die meisten Protokolle standardmäßig verwenden. Die Nonce ist 12 Byte lang; erzeugen Sie sie pro Nachricht zufällig. aAAD wird authentifiziert, aber nicht verschlüsselt, wodurch ein Chiffretext an seinen Kontext gebunden wird, zum Beispiel eine Nachrichten-ID oder eine Protokollversion, ohne diesen Kontext vor jemandem zu verbergen, der den Chiffretext sehen kann.

 


function sgcAES_GCM_Encrypt(const aKey, aIV, aPlain, aAAD: TBytes; out aTag: TBytes): TBytes;
function sgcAES_GCM_Decrypt(const aKey, aIV, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;

var
  oKey, oIV, oCipher, oTag, oPlain, oAAD: TBytes;
begin
  oIV := sgcRandomBytes(12);
  oCipher := sgcAES_GCM_Encrypt(oKey, oIV, oPlain, oAAD, oTag);
  { send oIV, oCipher and oTag to the recipient }
  if not sgcAES_GCM_Decrypt(oKey, oIV, oCipher, oAAD, oTag, oPlain) then
    { the ciphertext, the tag or the AAD was altered: reject it };
end;

 

ChaCha20-Poly1305 und XChaCha20-Poly1305

 

Das AEAD-Paar aus RFC 8439. Während AES-GCM auf AES-Hardwarebeschleunigung setzt, läuft ChaCha20-Poly1305 in reiner Software mit konstanter Geschwindigkeit, weshalb TLS und WireGuard es auf mobilen und eingebetteten Geräten ohne AES-NI standardmäßig verwenden. Die Nonce ist 12 Byte lang, dieselbe Einschränkung wie bei GCM. XChaCha20-Poly1305 erweitert die Nonce auf 24 Byte, groß genug, um sie für die Lebensdauer eines Schlüssels zufällig zu wählen, ohne einen Zähler mitzuführen, was wichtig ist, wenn sich viele unabhängige Schreiber einen Schlüssel teilen und einen Zähler nicht untereinander koordinieren können.

 


function sgcChaCha20Poly1305_Encrypt(const aKey, aNonce, aPlain, aAAD: TBytes; out aTag: TBytes): TBytes;
function sgcChaCha20Poly1305_Decrypt(const aKey, aNonce, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;
function sgcXChaCha20Poly1305_Encrypt(const aKey, aNonce, aPlain, aAAD: TBytes; out aTag: TBytes): TBytes;
function sgcXChaCha20Poly1305_Decrypt(const aKey, aNonce, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;

 

AES-CCM

 

Counter with CBC-MAC, RFC 3610 und NIST SP 800-38C. Dies ist die AEAD-Chiffre, die ressourcenbeschränkte und IoT-Stacks bevorzugen, weil sie nur die Blockchiffre selbst benötigt und keine GHASH-Tabelle: Zigbee, 802.15.4, Bluetooth und die TLS-CCM-Suiten schreiben sie alle vor. Wo Spielraum besteht, bleibt GCM die bessere Wahl, da CCM zwei Durchläufe über die Daten macht und nicht pipelinefähig ist.

 

Die Nonce ist 7 bis 13 Byte lang, und ihre Länge ist keine freie Wahl: 15 minus die Nonce-Länge ist die Größe des Längenfelds, was die maximale Nachrichtengröße festlegt, und eine 13-Byte-Nonce begrenzt den Klartext auf 65535 Byte. Eine Nonce darf unter einem Schlüssel niemals wiederholt werden, genau wie bei GCM. aTagLen beträgt 4, 6, 8, 10, 12, 14 oder 16 Byte.

 


function sgcAES_CCM_Encrypt(const aKey, aNonce, aPlain, aAAD: TBytes; aTagLen: Integer; out aTag: TBytes): TBytes;
function sgcAES_CCM_Decrypt(const aKey, aNonce, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;

var
  oKey, oNonce, oCipher, oTag, oPlain, oAAD: TBytes;
begin
  oNonce := sgcRandomBytes(12);
  oCipher := sgcAES_CCM_Encrypt(oKey, oNonce, oPlain, oAAD, 16, oTag);
  { send oNonce, oCipher and oTag to the recipient }
  if not sgcAES_CCM_Decrypt(oKey, oNonce, oCipher, oAAD, oTag, oPlain) then
    { the ciphertext, the tag or the AAD was altered: reject it };
end;

 

AES-GMAC

 

GMAC ist genau AES-GCM, ausgeführt mit leerem Klartext, wobei die gesamte Nachricht als zusätzliche Daten übergeben wird, so wie es der Standard definiert. Es erbt daher jede der Nonce-Regeln von GCM: aIV ist normalerweise 12 Byte lang und darf unter einem Schlüssel niemals wiederholt werden. Die Wiederverwendung eines IV unter GMAC schwächt es nicht nur, sie legt den Hash-Subkey offen und erlaubt einem Angreifer, Tags direkt zu fälschen, daher ist das Versagen vollständig statt allmählich. AES-CMAC auf der Seite Hashing & Nachrichtenauthentifizierung benötigt überhaupt keinen IV und ist die sicherere Wahl, sofern GCM nicht bereits Teil des Protokolls ist. Der Tag ist 16 Byte lang.

 


function sgcAES_GMAC(const aKey, aIV, aData: TBytes): TBytes;
function sgcAES_GMAC_Verify(const aKey, aIV, aData, aTag: TBytes): Boolean;

 

AES-Blockmodi

 

Diese bieten nur Vertraulichkeit, ohne Authentifizierung: Kombinieren Sie sie mit einem MAC aus Hashing & Nachrichtenauthentifizierung, wenn der Klartext Integrität benötigt, oder verwenden Sie stattdessen AES-GCM von oben.

 


function sgcAES_CBC_Encrypt(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CBC_Decrypt(const aKey, aIV, aCipher: TBytes): TBytes;
function sgcAES_CTR(const aKey, aIV, aData: TBytes): TBytes;
function sgcAES_CFB_Encrypt(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CFB_Decrypt(const aKey, aIV, aCipher: TBytes): TBytes;
function sgcAES_OFB(const aKey, aIV, aData: TBytes): TBytes;
function sgcAES_ECB_Encrypt(const aKey, aPlain: TBytes): TBytes;
function sgcAES_ECB_Decrypt(const aKey, aCipher: TBytes): TBytes;

 

sgcAES_CTR und sgcAES_OFB sind selbstinvers: Rufen Sie dieselbe Funktion erneut mit demselben Schlüssel und IV auf, um zu entschlüsseln. ECB ist nur zur Interoperabilität mit alten Formaten enthalten, die es vorschreiben; identische Klartextblöcke erzeugen identische Chiffretextblöcke, was die Struktur der Daten preisgibt, daher sollte es für nichts Neues gewählt werden.

 

CBC ist auch ganz ohne Padding verfügbar, der Modus, den Java als AES/CBC/NoPadding schreibt. Die Eingabelänge muss ein exaktes Vielfaches von 16 sein, und es wird eine Exception ausgelöst, wenn das nicht der Fall ist, statt dass der Aufruf still auffüllt und einen Chiffretext erzeugt, den die Gegenseite nicht lesen kann. Es ist nur dort sicher, wo das Protokoll bereits blockausgerichtete Daten garantiert.

 


function sgcAES_CBC_Encrypt_NoPad(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CBC_Decrypt_NoPad(const aKey, aIV, aCipher: TBytes): TBytes;

 

Ciphertext Stealing vermeidet den Padding-Overhead von CBC für Daten, die kein Vielfaches der Blockgröße sind:

 


function sgcAES_CTS_Encrypt(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CTS_Decrypt(const aKey, aIV, aCipher: TBytes): TBytes;

 

Padding-Schemata

 

Ein Blockmodus benötigt einen Klartext, der ein Vielfaches von 16 Byte ist, und das Padding-Schema ist der Weg dorthin. PKCS#7 ist die einzig richtige Wahl für etwas Neues; die anderen sechs existieren, um Daten aus einem System zu lesen und zu schreiben, das bereits anders entschieden hat. padPKCS5 steht nicht in der Liste, weil es für einen 16-Byte-Block identisch mit PKCS#7 ist, die beiden Namen unterscheiden sich nur in den Blockgrößen, für die sie definiert wurden.

 


type
  TsgcPadding = (padNone, padPKCS7, padZero, padANSIX923, padISO7816_4,
    padISO10126_2, padTBC);

function sgcPad_Add(const aData: TBytes; aBlockSize: Integer; aPadding: TsgcPadding): TBytes;
function sgcPad_Remove(const aData: TBytes; aBlockSize: Integer; aPadding: TsgcPadding; out aPlain: TBytes): Boolean;

function sgcAES_CBC_EncryptPad(const aKey, aIV, aPlain: TBytes; aPadding: TsgcPadding): TBytes;
function sgcAES_CBC_DecryptPad(const aKey, aIV, aCipher: TBytes; aPadding: TsgcPadding): TBytes;
function sgcAES_ECB_EncryptPad(const aKey, aPlain: TBytes; aPadding: TsgcPadding): TBytes;
function sgcAES_ECB_DecryptPad(const aKey, aCipher: TBytes; aPadding: TsgcPadding): TBytes;

var
  oKey, oIV, oPlain, oCipher: TBytes;
begin
  oCipher := sgcAES_CBC_EncryptPad(oKey, oIV, oPlain, padANSIX923);
end;

 

sgcAES_CBC_Encrypt und sgcAES_ECB_Encrypt weiter oben sind die PKCS#7-Kurzform dafür. sgcPad_Remove gibt bei einem fehlerhaften Padding False zurück, statt eine Exception auszulösen, und nimmt für jede Art von Fehler denselben Pfad, weil genau die Unterscheidung zwischen ihnen das ist, was ein Padding-Oracle benötigt. Besondere Vorsicht ist bei padZero geboten: Es kann ein Padding-Byte nicht von einer abschließenden Klartext-Null unterscheiden, daher entfernt es jede abschließende Null, und Daten, die legitim mit einer solchen enden können, kommen abgeschnitten zurück.

 

AES-Hardwarebeschleunigung (AES-NI)

 

Bei mit Delphi erstellten Windows-Builds (Win32 und Win64) prüft sgcCrypto_AES zur Laufzeit die CPU und führt, wenn sie AES-NI, PCLMULQDQ und SSSE3 meldet, die Einzelblock-Verschlüsselung, AES-CTR, AES-GCM (den Zählermodus und GHASH) und die AES-CBC-Verschlüsselung auf diesen Instruktionen aus, die von Natur aus konstant in der Zeit sind. Ohne sie, sowie unter Linux64, macOS, iOS und Android, wird wie bisher der Software-Kern mit konstanter Zeit verwendet. Die CBC-Entschlüsselung verwendet immer den Software-Kern. Die native TLS-Engine profitiert davon automatisch.

 

sgcAES_HardwareAvailable gibt True zurück, wenn dieser Build den Hardware-Pfad enthält und die CPU ihn unterstützt. Die globale Variable sgcAES_UseHardware, standardmäßig True, erzwingt bei False den Software-Kern, zum Beispiel um beide zu vergleichen. Setzen Sie sie einmal beim Start.

 

var sgcAES_UseHardware: Boolean = True;
function sgcAES_HardwareAvailable: Boolean;

if sgcAES_HardwareAvailable then
  Memo1.Lines.Add('AES-NI in use');

 

AES Key Wrap

 

Key Wrapping schützt einen Schlüssel, keine Nachricht: Es benötigt keinen IV, ist deterministisch und authentifiziert den umschlossenen Schlüssel. Dies ist die Standardmethode, um einen Datenverschlüsselungsschlüssel unter einem Master-Schlüssel zu speichern, zum Beispiel ein HSM- oder KMS-Master-Schlüssel, der einen Schlüssel pro Datei umschließt. RFC 3394 verlangt, dass der zu umschließende Schlüssel ein Vielfaches von 8 Byte ist; RFC 5649 fügt Padding für Schlüssel beliebiger Länge hinzu.

 


function sgcAES_KeyWrap(const aKEK, aPlainKey: TBytes): TBytes;
function sgcAES_KeyUnwrap(const aKEK, aWrapped: TBytes; out aPlainKey: TBytes): Boolean;
function sgcAES_KeyWrapPad(const aKEK, aPlainKey: TBytes): TBytes;
function sgcAES_KeyUnwrapPad(const aKEK, aWrapped: TBytes; out aPlainKey: TBytes): Boolean;

 

ChaCha20 und Salsa20 (rohe Stream-Chiffren)

 

Die Stream-Chiffren hinter den obigen AEAD-Konstruktionen, direkt bereitgestellt für Protokolle, die die rohe Chiffre vorschreiben. Keine von beiden authentifiziert: Fast immer sind die weiter oben auf dieser Seite beschriebenen Poly1305-AEAD-Versionen das, was Sie eigentlich wollen. Salsa20 ist der Vorgänger von ChaCha20, beibehalten für die Kompatibilität mit Formaten aus der NaCl-Ära; wählen Sie ChaCha20 für alles Neue.

 


function sgcChaCha20(const aKey, aNonce, aData: TBytes; aCounter: Cardinal): TBytes;
function sgcXChaCha20(const aKey, aNonce, aData: TBytes; aCounter: Cardinal): TBytes;
function sgcSalsa20(const aKey, aNonce, aData: TBytes; aCounter: Int64): TBytes;
function sgcXSalsa20(const aKey, aNonce, aData: TBytes; aCounter: Int64): TBytes;

 

Legacy: DES und NTLM

 

sgcCrypto_Legacy stellt auch einfaches DES im ECB-Modus und die DES-basierte NTLM-v1-Schlüsselableitung bereit. DES hat einen effektiven 56-Bit-Schlüssel, auf moderner Hardware innerhalb von Stunden per Brute Force zu brechen; er existiert nur zur Interoperabilität mit Protokollen, die ihn namentlich vorschreiben (NTLM-Authentifizierung ist der übliche Grund). Wählen Sie ihn für nichts Neues; verwenden Sie stattdessen AES.

 


function sgcDES_EncryptECB(const aKey, aData: TBytes): TBytes;
function sgcDES_DecryptECB(const aKey, aData: TBytes): TBytes;
function sgcDES_NTLM7to8(const aKey7: TBytes): TBytes;