Ders 14 / 17
Rastgelelik
Zayıf üreteç ile kriptografik olarak güvenli üretecin aynı ölçekte karşılaştırılması: tohum bilindiğinde on bin değerin tamamının önceden hesaplanması, on bin dört haneli koddan yalnız 903'ünün ayrı çıkması, baytı bölme kalanıyla basamağa eşlemenin ürettiği 1,04'lük sapma ve dokuz belirteç üretim noktasının altısının zayıf üreteç çağırması.
İçindekiler
Önceki dersin bütün ölçümleri bir şeyi verilmiş saydı: anahtarı ve başlangıç değerini üreten kaynağın tahmin edilemez olduğunu. O kaynak da kod tabanında bir çağrı noktasıdır ve on dört noktalık kriptografi envanterinde ayrı bir satır olarak görünmez. Saha uygulamasının yükleme belirteci, portalın parola sıfırlama bağlantısı ve dört haneli doğrulama kodu aynı soruyu paylaşır: üretilen değeri üçüncü bir taraf önceden hesaplayabilir mi?
Soru bir kalite sorusu değildir. Bir üreteç ya belirlenimcidir ya değildir ve arada bir bölge
yoktur. Aşağıdaki ölçümler iki üreteci aynı çekim kümesinde koşturur: biri kendi yazdığımız
doğrusal eşlenik üreteç (linear congruential generator), öteki node:crypto içindeki
kriptografik olarak güvenli üreteç.
İki Üreteç, Aynı Ölçü
KI4 — zayıf üreteç belirlenimcidir; durumu tohumdan tek bir aritmetik adımla ilerler, dolayısıyla tohumu bilen bir taraf üretilecek bütün değerleri önceden hesaplar. Kriptografik olarak güvenli üreteçte tahmin edilecek böyle bir tohum kalemi yoktur; çıktı işletim sisteminin entropi havuzundan beslenir.
KI5 — bir çekim kümesindeki ayrı değer sayısı üretecin gerçek çıktı genişliğini verir; sayı kuramsal beklentinin altına düşüyorsa üreteç nominal genişliğini kullanmıyordur. KI6 — rastgele baytı bölme kalanıyla bir aralığa eşlemek, aralık genişliği baytın alabileceği değer sayısını tam bölmüyorsa düzgün dağılım vermez.
// rast/uretec.mjs — zayif ureteci kriptografik olarak guvenli uretecle ayni olcekte // karsilastirir. Zayif uretec kendi yazdigimiz dogrusal eslenik uretectir; tohum gorunur. // Kriptografik uretecin ciktisi kosumdan kosuma degistigi icin ondan okunan degerler // dogrudan yazilmaz, girdiden turetilen bir bant denetiminden gecirilir. import { randomBytes, randomInt } from 'node:crypto'; const TOHUM = 20260301; const CEKIM = 10000; const uretecYap = (tohum) => { let durum = tohum; return () => (durum = (durum * 1103515245 + 12345) % 2 ** 31); }; // 1) Tohum bilindiginde kac deger onceden hesaplaniyor. const a = uretecYap(TOHUM), b = uretecYap(TOHUM); let ayni = 0; for (let i = 0; i < CEKIM; i++) if (a() === b()) ayni += 1; const g1 = randomBytes(4).readUInt32BE(0), g2 = randomBytes(4).readUInt32BE(0); console.log(`KI4 — tohum ${TOHUM} bilindiginde onceden hesaplanan deger: ${ayni}/${CEKIM}`); console.log(` kriptografik uretecte ayni tohum kalemi yok; iki cagri esit mi: ` + `${g1 === g2 ? 'evet' : 'hayir'}`); console.log(` saniye cozunurluklu saatten tohumlanan uretecte bir gunluk aday tohum: ${24 * 3600}`); // 2) Dort haneli dogrulama kodu: zayif uretecin alt basamaklari ile guvenli uretec. const kod = uretecYap(TOHUM); const zayif = new Set(); for (let i = 0; i < CEKIM; i++) zayif.add(kod() % 10000); const guvenli = new Set(); for (let i = 0; i < CEKIM; i++) guvenli.add(randomInt(0, 10000)); const bekleniyor = Math.round(10000 * (1 - (1 - 1 / 10000) ** CEKIM)); console.log(`\nKI5 — ${CEKIM} dort haneli kod, kuramsal beklenen ayri deger: ${bekleniyor}`); console.log(` zayif uretec, ayri kod : ${zayif.size}`); console.log(` guvenli uretec, ayri kod bant icinde mi: ` + `${Math.abs(guvenli.size - bekleniyor) < 120 ? 'evet' : 'hayir'} (${bekleniyor} +/- 120)`); // 3) Tek bayttan basamak turetmenin sapmasi: once tam sayim, sonra kosum. const yol = Array.from({ length: 10 }, (_, r) => Array.from({ length: 256 }, (_, x) => x).filter((x) => x % 10 === r).length); const ORAN = yol[0] / yol[9]; const CEKIM2 = 1000000; const sapmali = Array(10).fill(0), reddeden = Array(10).fill(0); let red = 0; for (const x of randomBytes(CEKIM2)) { sapmali[x % 10] += 1; // dogrudan bolum: 0-5 fazla cikar if (x < 250) reddeden[x % 10] += 1; else red += 1; // reddederek ornekleme } const oranla = (s) => (s.slice(0, 6).reduce((p, q) => p + q, 0) / 6) / (s.slice(6).reduce((p, q) => p + q, 0) / 4); const bant = (o, hedef) => (Math.abs(o - hedef) < 0.008 ? 'evet' : 'hayir'); const redBekleniyor = (CEKIM2 * 6) / 256; console.log(`\nKI6 — 256 bayt degerinin 10'a bolumunden kalan: 0-5 icin ${yol[0]} yol, ` + `6-9 icin ${yol[9]} yol, oran ${ORAN.toFixed(4)}`); const et = (ad) => ` ${ad.padEnd(46)}: `; console.log(et(`${CEKIM2} baytta dogrudan bolum, oran banti`) + `${bant(oranla(sapmali), ORAN)} (${ORAN.toFixed(4)} +/- 0.008)`); console.log(et('ayni baytlarda reddederek ornekleme, oran banti') + `${bant(oranla(reddeden), 1)} (1.0000 +/- 0.008)`); console.log(et('reddedilen bayt banti') + `${Math.abs(red - redBekleniyor) < 600 ? 'evet' : 'hayir'} (${redBekleniyor} +/- 600)`);
KI4 — tohum 20260301 bilindiginde onceden hesaplanan deger: 10000/10000 kriptografik uretecte ayni tohum kalemi yok; iki cagri esit mi: hayir saniye cozunurluklu saatten tohumlanan uretecte bir gunluk aday tohum: 86400 KI5 — 10000 dort haneli kod, kuramsal beklenen ayri deger: 6321 zayif uretec, ayri kod : 903 guvenli uretec, ayri kod bant icinde mi: evet (6321 +/- 120) KI6 — 256 bayt degerinin 10'a bolumunden kalan: 0-5 icin 26 yol, 6-9 icin 25 yol, oran 1.0400 1000000 baytta dogrudan bolum, oran banti : evet (1.0400 +/- 0.008) ayni baytlarda reddederek ornekleme, oran banti: evet (1.0000 +/- 0.008) reddedilen bayt banti : evet (23437.5 +/- 600)
Tohumun Verdiği Şey
Birinci ölçüm bir oran değil, bir eşitliktir: aynı tohumdan başlayan iki üreteç on bin çekimin on binini aynı sırayla üretiyor. Buradaki sayı önemsizdir; önemli olan onun tam sayı olmasıdır. Zayıf üreteç bir olasılık dağılımı değil, bir işlevdir — tohum verildiğinde çıktı dizisi tektir. Kriptografik üreteçte aynı ölçüm yapılamaz, çünkü karşılaştırılacak bir tohum kalemi yoktur.
Üçüncü satır tohumun neden ayrı bir kalem olduğunu söylüyor. Üreteç bir saatten tohumlanıyorsa — uygulama açılışında saniye çözünürlüklü bir değerden — bir günlük pencerede aday tohum sayısı 86.400’dür. Bu, çıktının ne kadar uzun olduğundan bağımsız bir tavandır: yüz yirmi sekiz bitlik görünen bir belirteç, on altı buçuk bitlik bir tahmin uzayı taşıyor olabilir. Uzunluk görünür bir niceliktir, tohum uzayı görünmez; kod incelemesinde belirtecin uzunluğuna bakmak bu yüzden yanıltıcıdır.
Kodun Kaç Değeri Var
Tahmin edilebilirlik bir evet-hayır niteliği değil, bir sayıdır ve o sayı üç ayrı yerden gelir: tohum uzayı, üretecin gerçekten dolaştığı çıktı uzayı ve çıktının istenen aralığa eşlenme biçimi. Üçü birbirinden bağımsızdır ve en küçüğü tavanı belirler. Birinci ölçüm ilkini saydı; ikinci ve üçüncü ölçüm kalan ikisini sayıyor.
İkinci ölçüm tohum hiç bilinmediğinde bile bir şey söylüyor. On bin dört haneli kod çekildiğinde kuramsal olarak 6321 ayrı değer beklenir; on bin kutuya on bin top atıldığında bazı kutulara birden fazla top düşer. Güvenli üreteç bu beklentiyi tutturuyor. Zayıf üreteç aynı çekimde yalnızca 903 ayrı kod üretiyor — beklenenin yedide birinden az.
Sebep, kodun üretilme biçimindedir: dört haneli kod üretecin çıktısının alt basamaklarından alınıyor ve bu üreteçte alt basamaklar üstlerden çok daha kısa bir çevrimde tekrarlanır. Nominal olarak on bin değerlik bir uzay vardır; gerçekte dokuz yüz civarında değer dolaşır. Bu sayının güvenlik karşılığı doğrudandır: doğrulama kodunu tahmin etmeye çalışan bir taraf için uzay on bin değil, dokuz yüzdür ve bunun için tohumu bilmesi gerekmez, yalnızca kodları toplaması yeter.
Basamak Türetmenin Sapması
Üçüncü ölçüm güvenli üretecin de yanlış çağrılabileceğini gösteriyor. Rastgele bir bayt 0–255 arasında değer alır; on tabanlı bir basamak isteyen kod bölme kalanını alırsa, 256 değerin kalanları eşit dağılmaz. Sıfırdan beşe kadar olan kalanlara 26 bayt değeri düşer, altıdan dokuza kadar olanlara 25. Oran 1,04’tür ve bu sayı ölçümden değil aritmetikten okunur; koşum yalnızca doğrular. Bir milyon baytta ölçülen oran bandın içinde çıkıyor.
Aynı baytlar reddederek örneklemeden geçirildiğinde — iki yüz elliden büyük ya da eşit bayt atılıp yenisi çekildiğinde — oran bire iniyor. Bedeli üçüncü satırda: bir milyon baytın 23.437,5 tanesi reddediliyor, yani her kırk üç bayttan biri. Yüzde iki buçukluk bir fazladan çekim, dört haneli kodun ilk basamağında yüzde dörtlük bir eğilimi kaldırıyor. Ölçünün burada söylediği şey şudur: güvenli üreteci çağırmak yetmez, çıktısını aralığa eşleyen satır de savunmanın parçasıdır ve o satır çoğunlukla merkezî kalıbın dışında yazılır.
Belirteç Üreten Noktalar
Envanter ölçüsüne dönmek gerekiyor. Kurgu ölçüm ağında belirteç, kimlik ya da dosya adı üreten dokuz nokta var. Aşağıdaki tablo her noktanın nominal genişliğini, üreteç seçimini ve tohum uzayıyla sınırlanan etkin genişliğini yan yana koyuyor.
// rast/belirtec.mjs — kurgu olcum aginda belirtec ureten noktalarin envanteri (model). // [nokta, tasidigi sey, uretec, nominal bit]. Zayif uretecin tahmin uzayi tohum uzayiyla // sinirlidir: saniye cozunurluklu saatten tohumlanan uretecte bir gunluk aday tohum 86400. const noktalar = [ ['portal/oturum-kimligi', 'kimlik', 'guvenli', 128], ['portal/parola-sifirlama', 'yetki', 'zayif', 128], ['portal/dogrulama-kodu', 'yetki', 'zayif', 13.3], ['portal/fatura-baglantisi', 'yetki', 'guvenli', 128], ['saha/yukleme-adi', 'yetki', 'zayif', 64], ['saha/is-emri-kimligi', 'kimlik', 'zayif', 32], ['alim/paket-kimligi', 'yok', 'zayif', 32], ['rapor/dosya-adi', 'yetki', 'zayif', 64], ['izleme/istek-kimligi', 'yok', 'guvenli', 64], ]; const TOHUM_UZAYI = Math.log2(24 * 3600); const etkin = ([, , uretec, bit]) => (uretec === 'zayif' ? Math.min(bit, TOHUM_UZAYI) : bit); const say = (kural) => noktalar.filter(kural).length; 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 D = [-26, 10, 10, 12, 12]; yaz(D, 'nokta', 'tasidigi', 'uretec', 'nominal bit', 'etkin bit'); for (const n of noktalar) yaz(D, n[0], n[1], n[2], n[3], etkin(n).toFixed(1)); const zayif = noktalar.filter(([, , u]) => u === 'zayif'); const sonuclu = zayif.filter(([, t]) => t !== 'yok'); console.log(`\nnokta ${noktalar.length}, zayif uretec ${zayif.length}, ` + `bunlarin kimlik ya da yetki tasiyani ${sonuclu.length}, tasimayani ${zayif.length - sonuclu.length}`); console.log(`toplam nominal bit ${noktalar.reduce((a, n) => a + n[3], 0).toFixed(1)}, ` + `toplam etkin bit ${noktalar.reduce((a, n) => a + etkin(n), 0).toFixed(1)}`); console.log(`en buyuk kayip: ${zayif.map((n) => [n[0], n[3] - etkin(n)]) .sort((x, y) => y[1] - x[1])[0].map((v) => (typeof v === 'number' ? v.toFixed(1) : v)).join(' ')} bit`); // Iki yeni belirtec noktasi ekleniyor: kalip zorunlu degilken ve zorunluyken. const yeni = ['portal/paylasim-baglantisi', 'saha/cevrimdisi-kuyruk-kimligi']; const E = [-34, 8, 10, 12, 16]; console.log('\niki yeni belirtec noktasi ekleniyor:'); yaz(E, 'duzen', 'nokta', 'kalipta', 'disarida', 'yeni acik adayi'); yaz(E, 'kalip var, dogrudan cagri serbest', noktalar.length + yeni.length, say(([, , u]) => u === 'guvenli'), say(([, , u]) => u === 'zayif') + yeni.length, yeni.length); yaz(E, 'kimlik ureten tek islev disa acik', noktalar.length + yeni.length, noktalar.length + yeni.length, 0, 0); console.log(`ikinci duzende once tasinmasi gereken nokta: ${zayif.length}`);
nokta tasidigi uretec nominal bit etkin bit portal/oturum-kimligi kimlik guvenli 128 128.0 portal/parola-sifirlama yetki zayif 128 16.4 portal/dogrulama-kodu yetki zayif 13.3 13.3 portal/fatura-baglantisi yetki guvenli 128 128.0 saha/yukleme-adi yetki zayif 64 16.4 saha/is-emri-kimligi kimlik zayif 32 16.4 alim/paket-kimligi yok zayif 32 16.4 rapor/dosya-adi yetki zayif 64 16.4 izleme/istek-kimligi yok guvenli 64 64.0 nokta 9, zayif uretec 6, bunlarin kimlik ya da yetki tasiyani 5, tasimayani 1 toplam nominal bit 653.3, toplam etkin bit 415.3 en buyuk kayip: portal/parola-sifirlama 111.6 bit iki yeni belirtec noktasi ekleniyor: duzen nokta kalipta disarida yeni acik adayi kalip var, dogrudan cagri serbest 11 3 8 2 kimlik ureten tek islev disa acik 11 11 0 0 ikinci duzende once tasinmasi gereken nokta: 6
Dokuz noktanın altısı zayıf üreteç çağırıyor ve bunların beşi kimlik ya da yetki taşıyor. Altıncısı — alım servisinin paket kimliği — tahmin edilebilir olmasının bir sonucu olmayan tek noktadır; o kimlikle hiçbir kapı açılmaz. Bu ayrım kapsamayı sayarken önemlidir: dışarıda kalan nokta sayısı altı, güvenlik sonucu doğuran nokta sayısı beştir ve kalıbı taşıma işi bu beşten başlar.
Etkin bit sütunu, nominal genişliğe bakmanın neden yanıltıcı olduğunu tek satırda gösteriyor. Parola sıfırlama bağlantısı yüz yirmi sekiz bitlik bir dizge üretiyor; tohum uzayı hesaba katıldığında geriye 16,4 bit kalıyor ve tek bir noktada 111,6 bitlik bir kayıp doğuyor. Toplamda 653,3 nominal bitin 415,3’ü ayakta kalıyor. Doğrulama kodu ise ilginç bir istisnadır: nominal genişliği zaten 13,3 bit olduğu için üreteç değişikliği ona bit kazandırmaz. O noktanın savunması üreteçte değil, deneme sayısını sınırlayan kuraldadır — ve o kural bu dersin kapsamının dışındadır.
Bu envanter önceki dersin on dört noktalık kriptografi envanteriyle bir yerde kesişiyor. Orada iki nokta “sabit başlangıç değeri” kusuruyla işaretlenmişti; sabit başlangıç değeri, üreteci hiç çağırmamak demektir ve o kalemin tahmin uzayını sıfıra indirir. Aynı üreteç iki envanterde birden görünür: bir yerde belirteç, öteki yerde şifreleme parametresi üretir. İki kalıbı ayrı ayrı taşımak aynı çağrıyı iki kez kapatmak demektir; iki envanteri tek bir “tahmin edilemez değer üreten” işleve bağlamak ise tek kapatma yapar ve bundan sonraki her yeni noktayı ikisinde birden kapsar.
Yeni Nokta Eklendiğinde
Son tablo iki yeni belirteç noktası ekliyor: portalın paylaşım bağlantısı ve saha uygulamasının çevrimdışı kuyruk kimliği. İlk düzende güvenli üreteci çağıran bir kalıp vardır ama zorunlu değildir. Nokta sayısı dokuzdan on bire çıkıyor, kalıptaki nokta üçte kalıyor, dışarıda kalan sekize çıkıyor ve iki yeni nokta doğrudan açık adayı oluyor. Yeni bir noktayı yazan kişi, kalıbın var olduğunu bilse bile üreteci kendi seçebilir; kapsama her eklemede düşer.
İkinci düzende kimlik üreten tek bir işlev dışa açılmıştır ve üreteç çağrısı başka hiçbir yerde görünmez. On bir noktanın on biri kalıptan geçer, yeni açık adayı sıfırdır. Bedel yine son satırdadır: bu düzene geçmeden önce zayıf üreteç kullanan altı noktanın altısını da taşımak gerekir. Kapsamayı sabit tutan şey kalıbın kalitesi değil, kalıbın tek yol olmasıdır.
Özet
- Aynı tohumdan başlayan iki zayıf üreteç on bin çekimin on binini aynı sırayla üretti; zayıf üreteç bir dağılım değil, bir işlevdir.
- Saniye çözünürlüklü bir saatten tohumlanan üreteçte bir günlük aday tohum sayısı 86.400’dür; bu, çıktının uzunluğundan bağımsız bir tahmin tavanı koyar.
- On bin dört haneli kodda kuramsal beklenti 6321 ayrı değerken zayıf üreteç 903 ayrı kod üretti; alt basamaklardan okumak nominal uzayı on kat daralttı.
- Baytı bölme kalanıyla basamağa eşlemek 0–5 için 26, 6–9 için 25 yol bırakıyor ve 1,04’lük bir eğilim doğuruyor; reddederek örnekleme bunu bire indiriyor, bedeli kırk üç bayttan biridir.
- Dokuz belirteç noktasının altısı zayıf üreteç çağırıyor, beşi kimlik ya da yetki taşıyor; parola sıfırlama bağlantısında tek başına 111,6 bitlik bir kayıp var.
- Kalıp zorunlu değilken iki yeni nokta kapsamayı üçte bırakıp iki açık adayı ekliyor; üreteç çağrısı tek işleve kapatıldığında on bir noktanın on biri kapsanıyor.
Sonraki Adım
Bu iki ders anahtarın ve belirtecin nasıl üretildiğini ölçtü ve ikisi de aynı varsayıma dayandı: üretilen değerin yalnız onu üreten süreçte kaldığına. Oysa o değerlerin bir bölümü hiç üretilmemiştir — bir yapılandırma satırında sabit yazılmış, oradan da sürüm denetimine girmiştir. Sonraki ders bu kalemi kurgu bir depo geçmişi üzerinde sayar: kaç işlemde sır kalmış, bir sır eklendiği andan döndürüldüğü ana kadar kaç gün açık durmuş, geçmişi temizlemek bunun ne kadarını kurtarıyor ve deseni tarayan bir kural kaç yanlış alarm üretiyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.