Simetrik Şifreleme

Simetrik şifreleme, şifrelemek ve şifresini çözmek için aynı anahtarı kullanır. Bu sayfada en çok önem taşıyan tek fikir, sade bir şifre ile bir AEAD (İlişkilendirilmiş Veriyle Kimlik Doğrulamalı Şifreleme) şifresi arasındaki farktır. Tek başına AES-CBC veya AES-CTR yalnızca gizlilik sağlar, başka hiçbir şey sağlamaz: düz metni okuyamayan bir saldırgan, yine de şifreli metindeki bitleri çevirebilir ve alıcı, herhangi bir şeyin yanlış olduğuna dair hiçbir belirti olmadan kurcalanmış sonucun şifresini çözer. AES-GCM ve ChaCha20-Poly1305, şifrelemenin yanı sıra kimlik doğrulama da yapar, bu yüzden kurcalama tespit edilir ve şifre çözme tamamen başarısız olur. Özel bir durum sade bir modu zorunlu kılmadıkça bir AEAD şifresini tercih edin.

 

Tüm fonksiyonlar sgcCrypto_AES, sgcCrypto_Modes, sgcCrypto_ChaCha ve sgcCrypto_Poly1305 içinde bulunur. Aynı anahtarla bir IV'yi veya bir nonce'u asla yeniden kullanmayın: CTR ve AEAD şifreleri için, yeniden kullanım yalnızca gizliliği zayıflatmaz; özellikle GCM için kimlik doğrulama garantisini tamamen kırar. Her mesaj için sgcRandomBytes ile yeni bir tane üretin.

 

AES-GCM (önerilir)

 

Çoğu protokolün varsayılan olarak kullandığı AEAD modu. Nonce 12 bayttır; mesaj başına rastgele üretin. aAAD kimlik doğrulanır ama şifrelenmez; bu, bir şifreli metnin, şifreli metni görebilen herkesten o bağlamı gizlemeden, bağlamına (örneğin bir mesaj kimliği veya bir protokol sürümü) nasıl bağlandığıdır.

 


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 ve XChaCha20-Poly1305

 

RFC 8439 AEAD çifti. AES-GCM, AES donanım hızlandırmasına dayanırken, ChaCha20-Poly1305 saf yazılımda sabit hızda çalışır; TLS ve WireGuard'ın AES-NI'siz mobil ve gömülü cihazlarda buna varsayılan olarak kullanmasının nedeni budur. Nonce 12 bayttır, GCM ile aynı kısıtlama. XChaCha20-Poly1305, nonce'u 24 bayta genişletir; bu, bir sayaç takip etmeden bir anahtarın ömrü boyunca rastgele seçilebilecek kadar büyüktür; bu da birçok bağımsız yazıcının bir anahtarı paylaştığı ve aralarında bir sayacı koordine edemediği durumlarda önem kazanır.

 


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

 

CBC-MAC ile sayaç, RFC 3610 ve NIST SP 800-38C. Bu, kısıtlı ve IoT yığınlarının tercih ettiği AEAD'dir, çünkü yalnızca blok şifrenin kendisine ihtiyaç duyar ve bir GHASH tablosuna ihtiyaç duymaz: Zigbee, 802.15.4, Bluetooth ve TLS CCM paketlerinin tümü bunu belirtir. Yer olduğunda GCM daha iyi seçim olmaya devam eder, çünkü CCM veri üzerinde iki geçiş yapar ve boru hattına alınamaz (pipelined).

 

Nonce 7 ila 13 bayttır ve uzunluğu serbest bir seçim değildir: 15 eksi nonce uzunluğu, uzunluk alanının boyutudur, bu yüzden en büyük mesaj boyutunu sabitler; 13 baytlık bir nonce, düz metni 65535 bayt ile sınırlar. Bir nonce, aynı anahtar altında asla tekrarlanmamalıdır, tıpkı GCM'de olduğu gibi. aTagLen, 4, 6, 8, 10, 12, 14 veya 16 bayttır.

 


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);
  { oNonce, oCipher ve oTag'i alıcıya gönder }
  if not sgcAES_CCM_Decrypt(oKey, oNonce, oCipher, oAAD, oTag, oPlain) then
    { şifreli metin, etiket veya AAD değiştirildi: reddet };
end;

 

AES-GMAC

 

GMAC, tam olarak boş bir düz metinle ve tüm mesaj ek veri olarak geçirilerek çalıştırılan AES-GCM'dir; standardın bunu tanımlama biçimi budur. Bu yüzden GCM'nin nonce kurallarının her birini devralır: aIV, normalde 12 bayttır ve aynı anahtar altında asla tekrarlanmamalıdır. Bir IV'yi GMAC altında yeniden kullanmak yalnızca onu zayıflatmakla kalmaz, hash alt anahtarını sızdırır ve bir saldırganın etiketleri doğrudan sahtelemesine izin verir, bu yüzden başarısızlık kademeli değil, tamamdır. Hash'leme ve Mesaj Kimlik Doğrulama sayfasındaki AES-CMAC hiç IV'ye ihtiyaç duymaz ve GCM zaten protokolde değilse daha güvenli seçimdir. Etiket 16 bayttır.

 


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

 

AES blok modları

 

Bunlar yalnızca gizlilik sağlar, kimlik doğrulama sağlamaz: düz metnin bütünlüğe ihtiyacı varsa Hash'leme ve Mesaj Kimlik Doğrulama sayfasından bir MAC ile eşleştirin, veya bunun yerine yukarıdaki AES-GCM'i kullanın.

 


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 ve sgcAES_OFB kendi tersidir: şifresini çözmek için aynı fonksiyonu aynı anahtar ve IV ile tekrar çağırın. ECB yalnızca bunu belirten eski biçimlerle birlikte çalışmak için dahil edilmiştir; aynı düz metin blokları aynı şifreli metin bloklarını üretir, bu da verinin yapısını sızdırır, bu yüzden yeni hiçbir şey için seçilmemelidir.

 

CBC ayrıca hiç dolgu olmadan da kullanılabilir; Java'nın AES/CBC/NoPadding olarak yazdığı mod budur. Girdi uzunluğu tam olarak 16'nın katı olmalıdır ve öyle olmadığında, çağrı sessizce dolgu ekleyip diğer tarafın okuyamayacağı bir şifreli metin üretmek yerine bir istisna fırlatır. Yalnızca protokolün zaten blok hizalı veri garanti ettiği yerlerde güvenlidir.

 


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

 

Şifreli metin çalma (ciphertext stealing), blok boyutunun bir katı olmayan veriler için CBC'nin dolgu (padding) ek yükünden kaçınır:

 


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

 

Dolgu şemaları

 

Bir blok modu, düz metnin 16 baytın katı olmasını gerektirir, ve dolgu (padding) şeması buna ulaşmanın yoludur. PKCS#7, yeni bir şey için tek doğru seçimdir; diğer altısı, zaten başka türlü seçmiş bir sistemden veri okumak ve yazmak için var olur. padPKCS5, listede değildir, çünkü 16 baytlık bir blok için bu PKCS#7'dir; iki isim yalnızca tanımlandıkları blok boyutlarında farklılık gösterir.

 


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 ve sgcAES_ECB_Encrypt, daha yukarıda, bunların PKCS#7 kısayoludur. sgcPad_Remove, raise etmek yerine bozuk bir dolguda False döndürür ve her tür başarısızlık için aynı yoldan geçer, çünkü bunları birbirinden ayırt etmek tam olarak bir padding oracle'ın ihtiyaç duyduğu şeydir. padZero ile özellikle dikkatli olun: bir dolgu baytını sondaki bir düz metin sıfırından ayırt edemez, bu yüzden sondaki her sıfırı siler, ve meşru olarak bir sıfırla bitebilecek veri kesilmiş olarak geri döner.

 

AES donanım hızlandırması (AES-NI)

 

Delphi ile yapılmış Windows Win32 ve Win64 derlemelerinde, sgcCrypto_AES çalışma zamanında CPU'yu denetler ve AES-NI, PCLMULQDQ ve SSSE3 bildirdiğinde, tek blok şifreleme, AES-CTR, AES-GCM (sayaç modu ve GHASH) ve AES-CBC şifrelemeyi, tasarım gereği sabit zamanlı olan bu komutlar üzerinde çalıştırır. Bunlar olmadan, ve Linux64, macOS, iOS ve Android'de, önceden olduğu gibi sabit zamanlı yazılım çekirdeği kullanılır. CBC şifre çözme her zaman yazılım çekirdeğini kullanır. Yerel TLS motoru bundan otomatik olarak yararlanır.

 

sgcAES_HardwareAvailable, bu derlemenin donanım yolunu taşıdığında ve CPU bunu desteklediğinde True döndürür. Varsayılan olarak True olan genel değişken sgcAES_UseHardware, False olarak ayarlandığında yazılım çekirdeğini zorlar, örneğin ikisini karşılaştırmak için. Bunu başlangıçta bir kez ayarlayın.

 

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

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

 

AES Key Wrap

 

Anahtar sarmalama bir anahtarı korur, bir mesajı değil: IV'ye ihtiyaç duymaz, deterministiktir ve sarmalanmış anahtarı kimlik doğrular. Bu, bir veri şifreleme anahtarını bir ana anahtar altında saklamanın standart yoludur; örneğin bir dosya başına anahtarı sarmalayan bir HSM veya KMS ana anahtarı. RFC 3394, sarmalanan anahtarın 8 baytın katı olmasını gerektirir; RFC 5649 herhangi bir uzunluktaki anahtarlar için dolgu ekler.

 


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 ve Salsa20 (ham akış şifreleri)

 

Yukarıdaki AEAD yapılarının arkasındaki akış şifreleri, ham şifreyi belirten protokoller için doğrudan sağlanır. Hiçbiri kimlik doğrulamaz: neredeyse her zaman bu sayfada daha yukarıdaki Poly1305 AEAD sürümleri istediğiniz şeydir. Salsa20, ChaCha20'nin öncülüdür; NaCl dönemi biçimleriyle uyumluluk için tutulur; yeni herhangi bir şey için ChaCha20'yi seçin.

 


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;

 

Eski: DES ve NTLM

 

sgcCrypto_Legacy ayrıca ECB modunda tekli DES ve DES tabanlı NTLM v1 anahtar türetmeyi de sağlar. DES, modern donanımda saatler içinde kaba kuvvetle kırılabilen 56 bit etkin bir anahtara sahiptir; yalnızca bunu isim olarak belirten protokollerle (genellikle NTLM kimlik doğrulaması bunun nedenidir) birlikte çalışmak için var olur. Yeni hiçbir şey için bunu seçmeyin; bunun yerine AES kullanın.

 


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