İçeriğe geç
academia.sh

Ders 03 / 21

Tuz ve Parola Saklama

İş çarpanı ile tuzun birbirinden bağımsız iki çarpan olarak ölçülmesi: N iki katına çıkınca birim maliyetin 16 katına kadar ölçülen bant içinde büyümesi, ön hesaplanmış tablonun 9 bitlik tuz eşiğinde kayıt başına baştan iş yapmakla eşitlenmesi, tuz uzunluğunu belirleyenin tablo boyutu değil doğum günü sınırı olması ve iki çarpanın da parolanın öngörülemezliği kökünü onaramaması.

İçindekiler

Önceki dersteki bütün ölçümler tek bir varsayıma dayanıyordu: girdiler sayaçla üretiliyordu ve her deneme eşit olasılıklıydı. Parola söz konusu olduğunda bu varsayım düşer. İnsanların seçtiği dizgiler olası girdi uzayına eşit dağılmaz; birkaç yüz milyonluk bir aday listesi, uzayın hesaplanabilir bir bölümünü kaplar. O durumda 2^256’lık ön görüntü sınırının hiçbir hükmü kalmaz, çünkü arama uzayın tamamında değil listenin içinde yapılır.

Bu derste ölçülen şey, o listenin nasıl pahalılaştırıldığıdır. İki ayrı çarpan vardır ve ikisi farklı şeyi çarpar. Aşağıdaki hesapların tamamı kendi modelimiz üzerindedir; gerçek bir parola kümesine karşı deneme yapmak, yazılı yetki ve tanımlı kapsam olmadan hem yasadışı hem meslek dışıdır.

Neyi Çarpan Çarpan

KT8: anahtar türetme fonksiyonunun iş çarpanı (work factor), tek bir denemenin birim maliyetini doğrusal olarak büyütür. Savunan taraf bu maliyeti giriş başına bir kez öder; deneyen taraf aday sayısı kadar öder. Asimetri buradadır.

KT9: ön hesaplanmış tablo (precomputed table) aynı tuzu paylaşan kayıtların tamamına birden uyar. Tuz kayıt başına ayrı üretilirse aynı tablo yalnızca tek bir kayda uyar.

İkisi çarpanın farklı yerine oturur. İş çarpanı deneme başına maliyeti, tuz ise tekrarlanacak deneme kümesi sayısını büyütür. Biri diğerinin yerine geçmez; birlikte kullanıldıklarında toplam iş ikisinin çarpımıdır. Bu ayrım, iki savunmanın ayrı ayrı ölçülmesini zorunlu kılar: iş çarpanı yükseltilip tuz atlanırsa toplam iş kayıt sayısına bölünür, tuz konulup çarpan düşük bırakılırsa birim maliyet ölçülemeyecek kadar küçük kalır.

Deneme Maliyeti Eğrisi

Aşağıdaki birim iş çarpanının maliyet üzerindeki etkisini ölçer. Ölçüm kalıbı özeldir: ham süre hiçbir yerde yazdırılmaz. Beklenen kat, girdiden — yani seçilen N değerlerinin oranından — türetilir; ölçülen değer yalnızca bir bant denetiminden geçirilir ve sonuç evet ya da hayır olarak yazılır. Bunun nedeni, mutlak sürenin makineye ve çalışma zamanına bağlı olmasıdır; ölçülmek istenen ise makineden bağımsız olan orandır.

// parola/carpan.mjs — is carpani ile deneme maliyeti egrisi. Sure ham olarak
// yazdirilmaz: beklenen kat girdiden (N oraninindan) turetilir, olculen deger yalnizca
// bant denetiminden gecirilip evet/hayir yazilir. Kurgu saha ekibi parolasi kullanilir.
import { scryptSync } from "node:crypto";

// KT8: anahtar turetme fonksiyonunun is carpani, tek bir denemenin birim maliyetini
//      dogrusal olarak buyutur; savunan taraf bu maliyeti bir kez, deneyen taraf aday
//      sayisi kadar oder.

const PAROLA = "kurgu-saha-ekibi-girisi";
const TUZ = Buffer.alloc(16, 3);                 // kurgu, sabit
const TABAN_US = 12;
const ayar = (us) => ({ N: 2 ** us, r: 8, p: 1, maxmem: 512 * 1024 * 1024 });
const olc = (us, kez) => {
  scryptSync(PAROLA, TUZ, 32, ayar(us));                       // isinma
  const t0 = process.hrtime.bigint();
  for (let i = 0; i < kez; i++) scryptSync(PAROLA, TUZ, 32, ayar(us));
  return Number(process.hrtime.bigint() - t0) / kez;
};
const taban = olc(TABAN_US, 8);
const ADAY = 1_000_000;                          // kurgu aday listesi buyuklugu
const yaz = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));
const G = [8, 9, 11, 13, 22];
console.log(`taban is carpani N=2^${TABAN_US}; birim = taban carpanda tek deneme`);
yaz(G, "N", "bellek", "beklenen", "bant icinde", "aday listesi (birim)");
for (const us of [12, 13, 14, 15, 16]) {
  const beklenen = 2 ** (us - TABAN_US);                       // girdiden turetildi
  const oran = olc(us, us > 14 ? 1 : 3) / taban;               // yalnizca bant denetimi
  const bantta = oran >= beklenen * 0.55 && oran <= beklenen * 1.8 ? "evet" : "hayir";
  yaz(G, "2^" + us, (128 * 8 * 2 ** us / 2 ** 20) + " MiB", beklenen + "x", bantta,
      (ADAY * beklenen).toLocaleString("tr-TR"));
}
taban is carpani N=2^12; birim = taban carpanda tek deneme
       N   bellek   beklenen  bant icinde  aday listesi (birim)
    2^12    4 MiB         1x         evet             1.000.000
    2^13    8 MiB         2x         evet             2.000.000
    2^14   16 MiB         4x         evet             4.000.000
    2^15   32 MiB         8x         evet             8.000.000
    2^16   64 MiB        16x         evet            16.000.000

Beş satırın beşinde ölçülen oran beklenen katın bandı içinde kaldı. Eğri doğrusaldır: N iki katına çıktığında hem birim maliyet hem de bellek tüketimi iki katına çıkar. Son sütun bunun deneyen taraftaki karşılığıdır — bir milyonluk aday listesinin tamamı taban çarpanda bir milyon birim, N=2^16 çarpanında on altı milyon birim tutar.

Asimetri bu tabloda görünmez ama tabloyu okumanın tek yolu odur. Savunan taraf çarpanı on altı katına çıkardığında giriş başına on altı kat öder ve bu, kullanıcının fark etmediği bir gecikmedir. Deneyen taraf aynı kararın bedelini aday başına öder. Bir tarafta günde birkaç bin giriş, öteki tarafta bir milyon aday vardır; aynı çarpan iki tarafta çok farklı toplamlara gider.

Asimetriyi sayıya çevirmek için iki tarafın toplamını yazmak yeterlidir. Kurgu ağda 340 saha hesabı var ve her hesap günde ortalama üç kez giriş yapıyor; bu, günde 1.020 doğrulamadır. N=2^16 çarpanında savunan tarafın günlük toplamı 16.320 birimdir. Deneyen taraf ise tuz nedeniyle listeyi her kayıt için baştan geçmek zorundadır: 340 × 16.000.000, yani 5,44×10^9 birim. İki toplamın oranı üç yüz binin üzerindedir. Çarpanı büyütmenin savunan tarafa maliyeti gündelik ve sabittir; deneyen tarafa maliyeti kayıt sayısıyla çarpılır.

Bellek sütunu ikinci ve ayrı bir kaldıraçtır. N=2^16 için her deneme 64 MiB tutar. Bu miktar tek bir doğrulama yapan sunucu için önemsizdir; çok sayıda denemeyi koşut yürütmek isteyen taraf için belirleyicidir, çünkü koşutluk artık işlem birimi sayısıyla değil bellek miktarıyla sınırlanır. İş çarpanı hesabı yavaşlatır, bellek katsayısı hesabı ucuz donanıma dağıtılamaz kılar.

Tablo Kaç Kalemde İşe Yaramaz Olur

İş çarpanının kapatmadığı bir yol vardır. Aday listesinin karmaları bir kez hesaplanıp saklanırsa, sızmış bir kayıt için yapılacak iş bir arama işlemine iner; hesap zaten yapılmıştır. Tuzun ölçülen etkisi tam olarak buradadır ve etkisi kalem sayısıyla yazılır.

// parola/tuz.mjs — on hesaplanmis tablonun tuz karsisinda kac kalemde ise yaramaz hale
// geldigi ve tuz uzunlugunu neyin belirledigi (model). Kurgu bolgesel olcum aginda 340
// saha ekibi hesabi var. Rastgelelik yok; her kosum ayni sayilari verir.

// KT9: on hesaplanmis tablo, ayni tuzu paylasan kayitlarin tamamina birden uyar; tuz
//      kayit basina ayri uretilirse ayni tablo yalnizca tek bir kayda uyar.
// KT10: tuz uzunlugunu belirleyen sey tablo boyutu degil, tuzlarin kendi aralarinda
//       cakismamasidir; olcut ikinci derste olculen dogum gunu sinirdir.

const ADAY = 1_000_000;                            // kurgu aday listesi
const KAYIT = 340;                                 // kurgu saha ekibi hesabi sayisi
const cift = (n) => (n * (n - 1)) / 2;
const yaz = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));
const say = (x) => x === 0 ? "0" : x >= 1e6 ? x.toExponential(2)
  : x >= 1 ? x.toLocaleString("tr-TR", { maximumFractionDigits: 2 })
  : x >= 1e-3 ? x.toFixed(3).replace(".", ",") : x.toExponential(1);

console.log(`aday listesi ${say(ADAY)}, kayit ${KAYIT}, kayit cifti ${say(cift(KAYIT))}`);
const G = [9, 10, 14, 14, 18];
yaz(G, "tuz biti", "ayri tuz", "tablo kalemi", "uydugu kayit", "beklenen cakisma");
for (const bit of [0, 8, 16, 32, 64, 128]) {
  const uzay = 2 ** bit;
  const ayri = Math.min(uzay, KAYIT);              // kullanimda olan ayri tuz sayisi
  yaz(G, bit, say(ayri), say(ADAY * ayri),         // tumunu kapsayan tablo kalemi
      say(Math.max(1, Math.round(KAYIT / uzay))), say(cift(KAYIT) / uzay));
}
const esik = Math.ceil(Math.log2(KAYIT));
console.log(`\ntuz uzayi ${KAYIT} kaydi astigi anda (${esik} bit) tablo kalemi ` +
  `${say(ADAY * KAYIT)} olur: kayit basina bastan is yapmakla ayni.`);

console.log("\ncakisma olcutu daha sikidir: beklenen tuz cakismasi 1'in altina insin");
const H = [12, 16, 12, 20];
yaz(H, "kayit", "kayit cifti", "en az bit", "128 bitte cakisma");
for (const n of [340, 12_400, 1e6, 1e9])
  yaz(H, say(n), say(cift(n)), Math.ceil(Math.log2(cift(n))), say(cift(n) / 2 ** 128));
aday listesi 1.00e+6, kayit 340, kayit cifti 57.630
 tuz biti  ayri tuz  tablo kalemi  uydugu kayit  beklenen cakisma
        0         1       1.00e+6           340            57.630
        8       256       2.56e+8             1            225,12
       16       340       3.40e+8             1             0,879
       32       340       3.40e+8             1            1.3e-5
       64       340       3.40e+8             1           3.1e-15
      128       340       3.40e+8             1           1.7e-34

tuz uzayi 340 kaydi astigi anda (9 bit) tablo kalemi 3.40e+8 olur: kayit basina bastan is yapmakla ayni.

cakisma olcutu daha sikidir: beklenen tuz cakismasi 1'in altina insin
       kayit     kayit cifti   en az bit   128 bitte cakisma
         340          57.630          16             1.7e-34
      12.400         7.69e+7          27             2.3e-31
     1.00e+6        5.00e+11          39             1.5e-27
     1.00e+9        5.00e+17          59             1.5e-21

İlk satır tuzsuz kurulumdur ve kritik sayı oradadır: bir milyon kalemlik tek bir tablo, 340 kaydın tamamına uyar. Yapılan iş bir kez yapılır ve kayıt sayısı kadar bölüşülür.

Sonraki satırlarda uydugu kayit sütunu 1’e iner ve bir daha yükselmez. Tuz sekiz bit olduğunda bile bir tablo artık ortalama tek bir kayda uymaktadır. Bu, tuzun asıl etkisinin uzunluktan değil kayıt başına ayrı olmasından geldiğini gösterir.

Bu ayrım kurulumdaki en yaygın kusuru da ölçer. Uygulama genelinde tek bir tuz kullanmak, tuzun uzunluğu 128 bit bile olsa tabloda sıfır bit satırına düşer: ayrı tuz sayısı birdir, tablo bir milyon kalemdir ve 340 kaydın tamamına uyar. Tuzu uzatmak bu satırı hiç değiştirmez; onu değiştiren tek şey tuzun kayıt başına ayrı üretilmesidir. Aynı biçimde, kullanıcı kodundan ya da kayıt zamanından türetilen tuz da tabloyu geri getirir, çünkü tablo o türetimi de önceden yapabilir.

Eşik tablo kalemi sütununda okunur. Tuz uzayı kayıt sayısını aştığı anda — burada dokuz bit — tabloyu bütün kayıtlara uydurmak 3,4×10^8 kalem ister ve bu, her kayıt için baştan iş yapmakla aynıdır. Dokuz bitten sonra sütun sabitlenir; daha uzun tuz, tabloyu daha fazla işe yaramaz kılmaz, çünkü zaten tamamen işe yaramaz durumdadır. Ön hesaplama, tuz karşısında 9 kalemde avantajını yitirir.

Tuz Uzunluğunu Ne Belirliyor

KT10: tuz uzunluğunu belirleyen şey tablo boyutu değil, tuzların kendi aralarında çakışmamasıdır; ölçüt önceki derste ölçülen doğum günü sınırıdır.

Dokuz bit yetiyorsa, yerleşik seçim neden on altı bayttır? İkinci tablo yanıtı verir ve ölçüt önceki dersten gelir. Tuz kayıt başına ayrı olmalıdır; iki kaydın aynı tuza düşmesi, o iki kayıt arasında tek tablo avantajını geri getirir. Aynı tuza düşme olasılığı ise doğum günü sınırına tabidir.

Tablo bunu sayıya çevirir. 340 kayıtta beklenen çakışmanın birin altına inmesi için en az on altı bit gerekir — dokuz değil. Kayıt sayısı 12.400’e çıkınca eşik yirmi yediye, bir milyona çıkınca otuz dokuza, bir milyara çıkınca elli dokuza gider. Eşik kayıt sayısıyla birlikte hareket eder ve bir kurulumda kaç kayıt olacağı yazılırken bilinmez.

On altı baytlık tuz bu belirsizliği ortadan kaldıran seçimdir. 128 bitte beklenen çakışma bir milyar kayıtta bile 1,5×10^-21’dir; kayıt sayısı hangi mertebeye giderse gitsin ölçüt sağlanır. Seçimin gerekçesi “daha uzun daha iyidir” değil, eşiğin hesaplanmasına gerek bırakmamasıdır. Tuz gizli olmadığı ve karmanın yanında saklandığı için uzatmanın bir maliyeti de yoktur: 340 kayıt için on altışar bayt, toplam 5.440 bayttır ve bu, tek bir kaydın geri kalanının yanında ölçülemeyecek kadar küçüktür. Son sütunun dört satırında da çakışma olasılığı sıfıra yuvarlanacak mertebede kalır; parametreyi bir kez fazlasıyla seçmek, her kurulumda yeniden hesaplamaktan ucuzdur.

İki Çarpanın Onaramadığı Kök

Bu dersin üçüncü sayısı, ilk ikisinin yetmediği yerdedir. İş çarpanı birim maliyeti çarpar, tuz tekrarlanacak küme sayısını çarpar; ikisi de aday listesinin kendisine dokunmaz.

Güven kökü buradadır ve adı şudur: parolanın aday listesinde bulunmaması. Kök tutuyorsa arama listede değil uzayın tamamında yapılır ve önceki dersin 2^n sınırı geçerli olur. Kök kırıldığında — yani parola listedeyse — kırılan tek kalem o hesabın kaydıdır, ama kırılmanın maliyeti listedeki sırasıyla orantılıdır. Bir milyonluk listenin başındaki bir aday, N=2^16 çarpanında birkaç birime bulunur; iş çarpanının on altı katı da bunu değiştirmez.

Kalem sayısı bu yüzden asimetriktir. Tuz ve iş çarpanı, 340 kaydın tamamını birden düşmekten korur: kırılma kayıt kayıt olur, toplu olmaz. Öngörülemezlik kökü ise kayıt başına ayrıdır ve tek bir kaydın kırılması diğer 339’unu etkilemez. Üç savunma üç ayrı şeyi yapar ve hiçbiri ötekinin yerini almaz.

Yenileme bedeli üç savunmada üç ayrı sayıdır. İş çarpanını yükseltmek elle sıfır kalemdir: kayıt, kullanıcının bir sonraki başarılı girişinde yeni çarpanla yeniden üretilir ve kimse bir şey yapmaz. Tuzu yenilemek de sıfır kalemdir, çünkü tuz zaten her parola değişikliğinde yenilenir. Kayıtların sızması durumunda ise yenileme 340 elle kalemdir — her hesabın parolası yeniden belirlenmek zorundadır ve bu, tek tek insan eylemi gerektirir. Sızıntı penceresinde duran meşru iş de sayılır: parolalar yenilenene kadar saha ekiplerinin girişi ya kapalı kalır ya da kırılmış sayılan bir kayıtla açık tutulur. Bu kursun üçüncü sütunu her derste aynı biçimde okunur — kırılmanın büyüklüğü değil, kırılmadan sonra elle dokunulan kalem sayısı.

Özet

  • İş çarpanı deneme başına birim maliyeti doğrusal büyütür: N 2^12’den 2^16’ya çıktığında beş ölçümün beşi de beklenen katın bandı içinde kalır ve bir milyonluk liste 1 milyon birimden 16 milyon birime çıkar.
  • Aynı karar iki tarafta farklı toplama gider: savunan taraf giriş başına, deneyen taraf aday başına öder; bellek katsayısı ayrıca koşutluğu bellekle sınırlar.
  • Tuzsuz kurulumda bir milyon kalemlik tek tablo 340 kaydın tamamına uyar; tuz kayıt başına ayrı olduğunda aynı tablo tek bir kayda uyar.
  • Ön hesaplama tuz karşısında 9 kalemde avantajını yitirir; bundan sonrası tabloyu daha işe yaramaz kılmaz.
  • Tuz uzunluğunu belirleyen ölçüt tablo boyutu değil çakışmadır: 340 kayıtta eşik 16 bit, bir milyarda 59 bittir; 128 bit eşiğin hesaplanmasını gereksiz kılar.

Sonraki Adım

Buraya kadarki üç ders tek yönlü işlemlerin etrafında döndü: karma geri dönmüyordu ve amaç zaten geri dönmemekti. Oysa kurgu ölçüm ağının asıl trafiği geri dönmek zorundadır — sahadan gelen okuma merkezde açılıp faturaya dönüşür. Birinci derste bu işlem iki kaleme bağlanmıştı: anahtar ve bozulmamış şifreli metin. Sonraki ders o birinci kalemi asıl sorunuyla birlikte ele alıyor. Aynı anahtarı iki tarafın da bilmesi gerekiyorsa, o anahtar oraya nasıl gitti? Altı şube ve merkez arasında kaç ayrı anahtar tutulur, ağa yeni bir şube eklendiğinde kaç kalem eklenir ve tek bir anahtar sızdığında kaç bağlantı yenilenmek zorunda kalır — hepsi sayılabilir.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat