İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat