Skip to content

Kriptografik Özet (Hash) Fonksiyonlarına Giriş

Asimetrik ve simetrik şifreleme algoritmaları verinin gizliliğini (confidentiality) sağlarken, dijital dünyada mesajların yolda değiştirilmediğinden (bütünlük - integrity) ve gönderenin gerçekten iddia ettiği kişi olduğundan (kimlik doğrulama - authentication) emin olmamız gerekir. İşte bu noktada modern kriptografinin "İsviçre çakısı" olarak bilinen Kriptografik Özet (Hash) Fonksiyonları devreye girer.

📌 Kısa Tarihçe

Hash fonksiyonu kavramı ilk olarak 1950'lerde Hans Peter Luhn tarafından veritabanlarında metinleri hızlıca aramak ve sınıflandırmak amacıyla bir bilgisayar algoritması olarak ortaya atıldı. 1976'da Diffie ve Hellman'ın asimetrik şifrelemeyi icadıyla birlikte dijital imzalarda kullanılmak üzere kriptografik bir boyut kazandı. Ancak modern kriptografik hash fonksiyonlarının (MD5, SHA serisi) matematiksel bel kemiğini, 1979 ve 1989 yıllarında birbirinden bağımsız olarak çalışan Ralph Merkle ve Ivan Damgård'ın kurduğu mimari oluşturmuştur.

🌍 Nerede ve Nasıl Kullanılır?

Özet fonksiyonları şifreleme yapmazlar (geri döndürülemezler); bunun yerine verinin benzersiz bir "dijital parmak izini" çıkarırlar. Kullanım alanları şunlardır:

  • Parola Saklama: Şifreleriniz veritabanlarında düz metin olarak değil, hash değerleri olarak saklanır. Veritabanı çalınsa bile hash'ten orijinal şifreye dönülemez.
  • Dosya Bütünlüğü (Checksum): İndirdiğiniz bir dosyanın eksik veya virüslü olup olmadığını kontrol etmek için dosyanın hash değeri yayıncının hash değeriyle karşılaştırılır.
  • Dijital İmzalar: Devasa bir PDF dosyasını RSA ile imzalamak saatler sürer. Bunun yerine dosyanın kısa hash'i alınır ve sadece bu hash imzalanır.
  • Blokzincir (Blockchain): Bitcoin ve Ethereum gibi ağlarda blokları birbirine kriptografik olarak bağlamak ve madencilik (Proof of Work) yapmak için yoğun olarak SHA-256 hash fonksiyonu kullanılır.

🔒 Matematiksel Tanım ve Güvenlik Kriterleri

Tanım: Kriptografik Özet (Hash) Fonksiyonu

Bir kriptografik hash fonksiyonu H, rastgele ve sonsuz uzunlukta olabilen herhangi bir m{0,1} mesajını girdi olarak alıp, sabit ve önceden belirlenmiş n uzunluğunda (örneğin 256 bit) bir h{0,1}n özet (hash) değerine dönüştüren deterministik bir fonksiyondur:

H:{0,1}{0,1}nh=H(m)

Bu fonksiyonun kriptografik olarak güvenli kabul edilebilmesi için şu üç direnç (resistance) şartını sağlaması zorunludur:

  1. Ön-görüntü Direnci (Pre-image Resistance - Tek Yönlülük): Sadece h hash değeri biliniyorken, H(m)=h şartını sağlayan orijinal m mesajını bulmak bilgisayarsal olarak imkansız olmalıdır.
  2. İkinci Ön-görüntü Direnci (Second Pre-image Resistance): Belirli bir m1 mesajı elimizdeyken, aynı hash değerini üreten farklı bir m2 mesajı (m1m2) bulmak (H(m1)=H(m2)) bilgisayarsal olarak imkansız olmalıdır.
  3. Çakışma Direnci (Collision Resistance): Hash değerleri aynı olan herhangi rastgele iki farklı mesaj (m1m2 ve H(m1)=H(m2)) bulmak bilgisayarsal olarak imkansız olmalıdır. (Güvercin Yuvası Prensibi gereği çakışmalar matematiksel olarak mevcuttur, ancak bulunmaları milyarlarca yıl sürmelidir).

📝 Çözümlü Uygulama: Basit Bir Hash Fonksiyonu

Özet fonksiyonlarının "ön-görüntü (pre-image)" bulma ve "çakışma (collision)" kavramlarını anlayabilmek için, modüler aritmetiğe dayanan çok basit bir hash fonksiyonu tanımlayalım. Gerçek dünyada ters görüntü bulmak imkansız olsa da, bu basit örnekte matematiğin nasıl işlediğini görebiliriz.

Örnek: Basit Hash Fonksiyonu ve Girdi (Ters Görüntü) Bulma

H:ZZ11 şeklinde tanımlanan ve kuralı H(x)(4x+5)(mod11) olan temel bir hash fonksiyonu verilmiştir.

a) Bu fonksiyon için hash özeti H(x)=2 olan bir girdi (ters görüntü) bulunuz. b) Bulduğunuz bu girdi ile çakışan (aynı hash değerini veren) farklı bir girdi daha bularak bir çakışma (collision) çifti oluşturunuz.

💡 Çözümü Göster / Gizle

a) Ters Görüntü (Pre-image) Bulma: Bizden H(x)2(mod11) şartını sağlayan bir x değeri bulmamız isteniyor. Denklemi kuralım:

4x+52(mod11)

Bilinenleri bir tarafa toplayalım (her iki taraftan 5 çıkaralım):

4x25(mod11)4x3(mod11)

Negatif sayıyı mod 11'de pozitif dengiyle değiştirelim (3+11=8):

4x8(mod11)

Eşitliğin her iki tarafını 4'e bölelim (çünkü 4 ile 11 aralarında asaldır):

x2(mod11)

Böylece H(x)=2 sonucunu veren girdi değerlerinden birinin x=2 olduğunu bulduk. Gerçekten de sağlamasını yaparsak: 4(2)+5=132(mod11).

b) Çakışma (Collision) Çifti Oluşturma: Modüler aritmetiğin doğası gereği, modül değerinin (11) tam katlarını eklediğimizde sonuç değişmeyecektir. Eğer x=2 için hash değeri 2 ise; x=2+11=13 girdisi için de hash değeri aynı çıkmalıdır.

Sağlamasını yapalım:

H(13)=4(13)+5=52+5=5757=(115)+2572(mod11)

Sonuç: H(2)=2 ve H(13)=2 bulunmuştur. 213 olmasına rağmen aynı hash değerini ürettikleri için {2,13} kümesi bu fonksiyon için bir çakışma (collision) çiftidir.


🏗️ Merkle-Damgård Mimarisinin (Construction) Anatomisi

Matematikçiler, "sonsuz uzunluktaki bir veriyi, sabit uzunlukta güvenli bir çıktıya dönüştüren bir fonksiyonu tek seferde nasıl yazarız?" sorusuyla karşılaştılar. Merkle ve Damgård, bu sorunu harika bir zincirleme mantığıyla çözdü. Günümüzde MD5, SHA-1 ve SHA-2 ailelerinin kullandığı bu mimari, süreci küçük bloklara böler.

İşleyiş Mantığı:

  1. Doldurma (Padding): Gelen sonsuz mesaj, önce önceden belirlenmiş blok boyutunun tam katı olacak şekilde sonuna anlamsız veriler eklenerek tamamlanır. Ayrıca güvenlik gereği, eklenen bu dolgunun en sonuna mesajın orijinal uzunluğu da yazılır (Buna Merkle-Damgård Strengthening denir).
  2. Sıkıştırma Fonksiyonu (Compression Function): Mimari, koca bir veriyi tek seferde yutmaya çalışmaz. Bunun yerine sadece belirli uzunlukta bir mesaj bloğu ve bir önceki adımın sonucunu alıp sabit uzunlukta çıktı üreten küçük bir f fonksiyonu kullanır.
  3. Zincirleme (Chaining): Sisteme başlangıç için sabit bir Başlangıç Vektörü (IV - Initialization Vector) olan h0 verilir. Mesaj bloklara ayrılır ve tıpkı bir döngü gibi her blok sırayla sıkıştırma fonksiyonundan geçirilir.

🔍 Ekstra Açıklama: Zincirleme Hesaplama Gerçekte Nasıl Çalışır?

Bu sistemi bir bayrak yarışı gibi düşünebilirsiniz. Elimizde uzun bir mesaj var ve biz bunu m1, m2 ve m3 adında üç parçaya (bloğa) böldük. Yarış şu şekilde ilerler:

  • Isınma (Başlangıç): Yarışa başlamadan önce elimizde sistemin belirlediği sabit bir değer (Başlangıç Vektörü, h0) vardır.
  • 1. Aşama: Fonksiyon, ilk mesaj parçası olan m1'i ve başlangıç değerimiz olan h0'ı alır. Bu ikisini karıştırır (sıkıştırır) ve yeni bir değer olan h1'i üretir.
    • Matematiksel karşılığı: f(h0,m1)h1
  • 2. Aşama: Artık yeni bayrak h1'dir. Fonksiyon sıradaki mesaj bloğunu (m2) alır ve onu bir önceki adımın sonucu olan h1 ile karıştırarak h2'yi üretir.
    • Matematiksel karşılığı: f(h1,m2)h2
  • 3. Aşama (Bitiş): Son mesaj bloğumuz m3, bir önceki adımın sonucu olan h2 ile karıştırılır. Başka blok kalmadığı için ortaya çıkan h3 değeri yarışı bitirir.
    • Matematiksel karşılığı: f(h2,m3)h3

Sonuç: Zincirin en sonundaki adımda elde ettiğimiz h3 değeri, tüm mesajın nihai Hash (özet) değeridir. Yani önceki her harf/blok, kendinden sonraki tüm hesaplamayı kelebek etkisi gibi değiştirmiştir.

📌 Ufak Bir Not: Merkle-Damgård Güvenliği

Eğer sistemin tam kalbinde çalışan bu ufak blok bazlı sıkıştırma fonksiyonu (f) çakışmaya dayanıklı (collision-resistant) ise, bu zincirleme mimariyle kurulan devasa ana özet fonksiyonu da matematiksel olarak çakışmaya dayanıklı kabul edilir.


📝 Çözümlü Uygulamalar: MD Yapısı

Örnek: Bit Düzeyinde Sıkıştırma ve Merkle-Damgård Zincirlemesi

H~:{0,1}3{0,1}2 şeklinde tanımlanan bir sıkıştırma fonksiyonu verilmiştir.

Bu fonksiyonun çıktı kuralı şu şekildedir: İlk bit, girdideki bitlerin toplamının (mod 2) değerini (parite); ikinci bit ise girdinin ortasındaki (ikinci) biti temsil eder.

x (Girdi)H~(x) (Çıktı)
00000
00110
01011
01101
10010
10100
11001
11111

a) H~ sıkıştırma fonksiyonu için bir çakışma (collision) çifti bulunuz. b) Başlatma vektörü (IV) h1=00 olan ve H~ kullanılarak Merkle-Damgård yapısı ile oluşturulan genel H hash fonksiyonu için H(1101) değerini hesaplayınız. c) H(1101) ile çakışan, farklı uzunlukta bir girdi bulunuz.

💡 Çözümü Göster / Gizle

a) Çakışma Analizi (Sıkıştırma Fonksiyonu için): Çakışma, H~(x)=H~(x) ve xx durumunda gerçekleşir. Doğruluk tablosu incelendiğinde birden fazla çakışma görülmektedir:

  • H~(001)=10 ve H~(100)=10 {001,100} bir çakışmadır.
  • H~(010)=11 ve H~(111)=11 {010,111} bir çakışmadır.

b) Merkle-Damgård Hesaplaması (H(1101)): Sıkıştırma fonksiyonumuz 3 bit girdi alıp 2 bit çıktı veriyor. Her iterasyonda bir önceki adımın 2 bitlik çıktısı ile mesajın sıradaki 1 biti birleştirilir (Concatenation, ).

Girdi mesajımız x=x1x2x3x4=1101 için adımlar (Girdi blok uzunluğu c=1, durum uzunluğu b=2):

  1. Adım 1 (Başlangıç): h1=00
  2. Adım 2 (x1=1 işlenir): h2=H~(h1x1)=H~(001)=H~(001)=10
  3. Adım 3 (x2=1 işlenir): h3=H~(h2x2)=H~(101)=H~(101)=00
  4. Adım 4 (x3=0 işlenir): h4=H~(h3x3)=H~(000)=H~(000)=00
  5. Adım 5 (x4=1 işlenir): h5=H~(h4x4)=H~(001)=H~(001)=10

Nihai Sonuç: Zincirin son elemanı hash değerimizdir. H(1101)=10.

c) Hash Çakışması Bulma (Ana Fonksiyon için): Hesaplama sürecindeki ara değerlere (durumlara) bakıldığında, 2. adımda elde edilen h2=10 değeri dikkat çekicidir. Bu ara değer, aslında sadece x=1 (tek bitlik) girdisinin hash özetidir:

H(1)=H~(h11)=H~(001)=H~(001)=10

Böylece H(1)=10 ve H(1101)=10 olduğu saptanmıştır. 11101 olduğundan ve farklı uzunluklarda olmalarına rağmen aynı sonucu verdiklerinden, {1,1101} çifti genel H hash fonksiyonu için bir çakışma teşkil eder.

Örnek: Metin Tabanlı Merkle-Damgård Zincirlemesi

Kendi tasarımımız olan ve Merkle-Damgård mimarisini kullanan H özet fonksiyonu ile m="MATH" mesajının hash değerini, yani H(MATH) sonucunu bulunuz.

Sistem Kuralları ve H Fonksiyonunun Tanımı:

  • H fonksiyonu, gelen m mesajını 1 harflik bloklara bölerek işler. Dolayısıyla 4 harfli m="MATH" girdisi için m1=M,m2=A,m3=T,m4=H olmak üzere zincirleme mimari gereği 4 iterasyon gereklidir.
  • Harflerin sayısal değerleri 0-25 tablosuna göredir (A=0, M=12, T=19, H=7).
  • Sistemin Başlangıç Vektörü (IV) h0=5 olarak sabitlenmiştir.
  • İçeride çalışan sabit boyutlu sıkıştırma fonksiyonu f, bir önceki iterasyonun durumu ile yeni mesaj bloğunu şu formülle birleştirir:f(hi1,mi)=(hi13+mi)(mod26)
  • Buna göre genel hash fonksiyonumuz H(m), Merkle-Damgård'ın birbirini çağıran zincirleme yapısı gereği şu formülle tanımlanır:H(m)=f(f(f(f(h0,m1),m2),m3),m4)=h4
💡 Çözümü Göster / Gizle

İstenen H(MATH) değerini bulmak için H fonksiyonunun iç yapısındaki f sıkıştırma adımını sırasıyla 4 kez (her bir blok için) uygulayacağız. Başlangıç durumumuz h0=5'tir.

1. İterasyon (m1=M12):

h1=f(h0,m1)h1(53+12)(mod26)h1=15+12=27271(mod26)h1=1

2. İterasyon (m2=A0): Bir önceki adımın zincir çıktısı olan h1=1 kullanılarak fonksiyon tekrar çalıştırılır.

h2=f(h1,m2)h2(13+0)(mod26)h2=3+0=3h2=3

3. İterasyon (m3=T19): Bir önceki çıktımız olan h2=3 devreye girer.

h3=f(h2,m3)h3(33+19)(mod26)h3=9+19=28282(mod26)h3=2

4. İterasyon (m4=H7): Son mesaj bloğumuz olan H harfi, h3=2 değeriyle birlikte işlenir.

h4=f(h3,m4)h4(23+7)(mod26)h4=6+7=13h4=13

Sonuç: Bütün mesaj blokları iç içe geçmiş bir şekilde zincirleme kuralla (Merkle-Damgård) işlendiği için son iterasyon değeri bütünüyle H(m)'e eşittir.

H(MATH)=h4=13

Tabloya göre 13 sayısının karşılığı N harfidir. Böylece "MATH" kelimesinin sistemimizdeki nihai hash (özet) değeri N olarak hesaplanmıştır.

Akademik amaçlarla tasarlanmış açık kaynaklı eğitim arşivi. Bu sitedeki tüm ders notları ve içerikler CC BY-NC-SA 4.0 Lisansı ile korunmaktadır.