Parola Hash'leme ve Anahtar Türetme

Bu dört fonksiyon, karıştırılması kolay iki farklı sorunu çözer.

 

Bir parolayı saklamak yavaş ve belleğe zor olmalıdır, böylece veritabanının çalınması parolaları ele vermez. Önce Argon2id'yi, sonra scrypt'i kullanın ve PBKDF2'yi yalnızca bir standart özellikle bunu gerektirdiğinde kullanın.

 

Zaten rastgele, yüksek entropili bir gizli değerden (örneğin bir X25519 anahtar anlaşmasının çıktısından) anahtar türetmek hızlı olmalıdır: doğru araç HKDF'dir ve yavaş bir parola hash'ini zaten rastgele olan bir gizli değere yöneltmek, hiçbir güvenlik kazancı olmadan CPU zamanı israf eder.

 

Tüm fonksiyonlar sgcCrypto_KDF, sgcCrypto_Scrypt, sgcCrypto_Argon2 ve sgcCrypto_HKDF içinde bulunur.

 

PBKDF2

 

Bir HMAC'ı çok kez tekrarlayarak bir parolayı uzatır. Yalnızca CPU zamanına mal olur, bu yüzden özel donanım (GPU'lar, FPGA'lar) buna, meşru bir sunucunun tek bir parolayı kontrol etmesine mal olduğundan çok daha ucuza saldırır. Buradaki üç parola hash'inin en zayıfıdır; bazı standartların (WPA2, birkaç PKCS biçimi) hâlâ bunu isim olarak gerektirmesi nedeniyle tutulur.

 


function sgcPBKDF2_SHA1(const aPassword, aSalt: TBytes; aIterations, aDKLen: Integer): TBytes;
function sgcPBKDF2_SHA256(const aPassword, aSalt: TBytes; aIterations, aDKLen: Integer): TBytes;
function sgcPBKDF2_SHA384(const aPassword, aSalt: TBytes; aIterations, aDKLen: Integer): TBytes;
function sgcPBKDF2_SHA512(const aPassword, aSalt: TBytes; aIterations, aDKLen: Integer): TBytes;

 

scrypt

 

N, bellek/CPU maliyet parametresidir: r=8 ile 16384, hash başına kabaca 16 MB RAM gerektirir; bu da büyük ölçekli bir GPU saldırısını yalnızca yavaş değil, pahalı hale getiren şeydir, çünkü saldırganın yalnızca hesaplama çekirdekleri için değil, bellek için de ödeme yapması gerekir.

 


function sgcScrypt(const aPassword, aSalt: TBytes; aN, aR, aP, aDKLen: Integer): TBytes;

var
  oHash: TBytes;
begin
  oHash := sgcScrypt(TBytes(oPassword), oSalt, 16384, 8, 1, 32);
end;

 

Argon2

 

Argon2, 2015 Parola Hash'leme Yarışması'nı kazandı ve parola saklamak için şu anki ilk seçimdir. Argon2id kullanılacak varyanttır: Argon2d'nin veriye bağımlı adreslemesini (GPU kırmaya karşı güçlü) Argon2i'nin veriden bağımsız ilk geçişiyle (bellek erişim örüntülerini gözlemleyebilen bir yan kanal saldırganına karşı dirençli) harmanlar. RFC 9106, etkileşimli oturum açmalar için en az 19 MiB bellek ve 2 yineleme, veya ekstra maliyetin kabul edilebilir olduğu yerlerde 64 MiB ve 3 yineleme önerir.

 


function sgcArgon2id(const aPassword, aSalt: TBytes; aMemoryKB, aIterations, aParallelism, aTagLen: Integer): TBytes;

var
  oStored, oCandidate: TBytes;
begin
  { at signup }
  oStored := sgcArgon2id(TBytes(oPassword), oSalt, 19456, 2, 1, 32);

  { at login }
  oCandidate := sgcArgon2id(TBytes(oTypedPassword), oSalt, 19456, 2, 1, 32);
  if sgcConstantTimeEquals(oStored, oCandidate) then
    { password accepted };
end;

 

Argon2d ve Argon2i, belirli bir varyanta ihtiyaç duyan çağıranlar için genel giriş noktası üzerinden de kullanılabilir:

 


function sgcArgon2(aKind: TsgcArgon2Kind; const aPassword, aSalt, aSecret, aAssociated: TBytes;
  aMemoryKB, aIterations, aParallelism, aTagLen: Integer): TBytes;

 

HKDF

 

HKDF, tek bir iyi gizli değeri bir protokolün ihtiyaç duyduğu kadar bağımsız anahtara böler. aInfo parametresi bunları bağımsız tutan şeydir: farklı bir bilgi dizesine sahip aynı gizli değer, ilgisiz bir anahtar üretir; bu da bir amaç için türetilen bir anahtarın (örneğin bir mesajı şifrelemek) başka bir amaç için de (örneğin birini kimlik doğrulamak için) kullanılabilir olmasını tam olarak engelleyen şeydir.

 


function sgcHKDF_SHA256(const aIKM, aSalt, aInfo: TBytes; aLen: Integer): TBytes;
function sgcHKDF_SHA384(const aIKM, aSalt, aInfo: TBytes; aLen: Integer): TBytes;

var
  oSharedSecret, oSalt, oClientKey, oServerKey: TBytes;
begin
  oClientKey := sgcHKDF_SHA256(oSharedSecret, oSalt, TBytes(AnsiString('client write key')), 32);
  oServerKey := sgcHKDF_SHA256(oSharedSecret, oSalt, TBytes(AnsiString('server write key')), 32);
end;

 

sgcHKDF_SHA256'nın arkasındaki extract ve expand adımları, bunları farklı zamanlarda çalıştırması gereken protokoller için ayrı ayrı da kullanılabilir:

 


function sgcHKDF_Extract_SHA256(const aSalt, aIKM: TBytes): TBytes;
function sgcHKDF_Expand_SHA256(const aPRK, aInfo: TBytes; aLen: Integer): TBytes;

 

KDF1, KDF2 ve ANSI X9.63

 

ISO 18033-2 ve IEEE 1363a'dan üç sayaç modu anahtar türetme fonksiyonu, bunlar da sgcCrypto_KDF içindedir. Bunlar, RSA-KEM'in, ECIES'in ve daha eski ANSI şemalarının isim olarak belirttiği fonksiyonlardır; bunlara başvurmanın tek nedeni budur. Yukarıdaki HKDF, yeni herhangi bir şey için daha iyi seçim olmaya devam eder, çünkü gizli değeri doğrudan çıktıya hash'lemek yerine çıkarmayı (extraction) genişletmeden (expansion) ayırır.

 

KDF1 ve KDF2, tam olarak tek bir ayrıntıda farklılık gösterir, ve bu gerçek bir birlikte çalışabilirlik tuzağıdır: KDF1 sayacına 0'dan başlar, KDF2 1'den başlar. Çıktılar tamamen farklıdır, her fonksiyon tek başına doğru görünür, ve bir spesifikasyon her zaman hangisini kastettiğini açıkça belirtmez, bu yüzden isme değil spesifikasyona bakın. X9.63, sayaçtan sonra eklenen bir paylaşılan-bilgi (shared-info) dizesiyle KDF2'dir; bu, ECIES'in ve daha eski ECDH TLS paketlerinin belirttiği biçimdir.

 


function sgcKDF1(const aSeed: TBytes; aLen: Integer; aHash: TsgcKDFHash): TBytes;
function sgcKDF2(const aSeed: TBytes; aLen: Integer; aHash: TsgcKDFHash): TBytes;
function sgcKDF_X963(const aSharedSecret, aSharedInfo: TBytes; aLen: Integer; aHash: TsgcKDFHash): TBytes;

var
  oSharedSecret, oSharedInfo, oKey: TBytes;
begin
  { ECIES: ECDH X koordinatı artı şemanın tanımladığı paylaşılan bilgi }
  oKey := sgcKDF_X963(oSharedSecret, oSharedInfo, 32, khSHA256);
end;