Modern kriptografik primitiflere derinlemesine dalış — ECDSA, SHA-256, ChaCha20-Poly1305, AES-GCM, Argon2id, X25519, HKDF ve daha fazlası. Çalıştırılabilir kod örnekleriyle.
Altındaki primitifleri anlayana kadar her şifreleme kütüphanesi bir kara kutudur. Bu belge projelerimde — Occlude, ATU Humidor, Fuarcat — kullandığım algoritmaları parçalarına ayırıyor ve her birinin kod düzeyinde nasıl çalıştığını gösteriyor.
Primitiflere geçmeden önce, her ailenin hangi problemi çözdüğünü içselleştir:
Özellik
Anlamı
Primitive
Gizlilik (Confidentiality)
Yalnızca hedeflenen alıcılar okuyabilir
Simetrik/asimetrik şifreleme
Bütünlük (Integrity)
Veri manipüle edilmemiştir
Hash fonksiyonları, MAC’lar
Özgünlük (Authenticity)
Gönderen iddia ettiği kişidir
MAC’lar, dijital imzalar
İnkar Edilemezlik (Non-repudiation)
Gönderen gönderdiğini inkâr edemez
Dijital imzalar (MAC’lar değil)
İleriye Dönük Gizlilik (Forward Secrecy)
Uzun vadeli anahtarın ele geçirilmesi geçmiş oturumları açığa çıkarmaz
Geçici anahtar değişimi (ECDH)
MAC’lar özgünlük sağlar ama inkar edilemezlik sağlamaz — her iki taraf da anahtarı paylaşır, dolayısıyla ikisi de etiketi üretmiş olabilir. İmzalar asimetrik anahtarlar kullanır, dolayısıyla yalnızca özel anahtar sahibi imzalamış olabilir.
Güvenlik tamamen anahtara dayanmalıdır, algoritmaya değil. Saldırganın şifreni her ayrıntısıyla bildiğini varsay. Güvenlik algoritmanın gizli kalmasına bağlıysa (“gizlilik yoluyla güvenlik”), başarısız olur.
Tüm kriptografik işlemler, gerçek rastgelelikten hesaplama açısından ayırt edilemeyen rastgele sayılar gerektirir. Math.random() asla kullanma — kriptografik olarak güvenli değildir.
csprng.ts
// Node.js / Edge runtime'larimport { randomBytes } from "node:crypto";const key = randomBytes(32); // 256-bit anahtarconst nonce = randomBytes(12); // GCM için 96-bit nonceconst salt = randomBytes(16); // 128-bit salt// Web Crypto API (tarayıcı / Cloudflare Workers / Deno)const key2 = crypto.getRandomValues(new Uint8Array(32));const nonce2 = crypto.getRandomValues(new Uint8Array(12));// Noble (evrensel — arka planda WebCrypto kullanır)import { randomBytes as nobleRandom } from "@noble/ciphers/webcrypto";const nonce3 = nobleRandom(24); // Uint8Array döner
Kural: Her zaman işletim sistemi destekli entropi kullan (OsRng, crypto.getRandomValues, /dev/urandom). Zamanla tohumlanan thread-local PRNG’ler kırık sayılır.
İşletim sistemi CSPRNG’si donanım olaylarından beslenir (kesme zamanlaması, CPU titremesi, x86’da RDRAND). Linux’ta: /dev/urandom (engellemesiz, tüm kriptografi için uygun). /dev/random bloke eder — modern kodda asla kullanma, ek güvenlik sağlamaz.
Bütünlük doğrulamanın temel taşı. Rastgele girdiden sabit 256-bit özet üretir. HMAC, anahtar türetme, dijital imzalar ve içerik adresleme için kullanılır.
Özet boyutu iki katı. Ed25519 imza şemalarında HMAC-SHA-512 için dahili hash ve daha yüksek güvenlikli KDF’ler için kullanılır. SHA-384 de mevcuttur (kırpılmış SHA-512, 64-bit donanımda biraz daha hızlı).
SHA-2’den tamamen farklı bir yapı — Merkle-Damgård değil, sünger tabanlı. SHA-3, HMAC olmadan kullanılan SHA-2’yi etkileyen uzunluk uzatma saldırılarına karşı savunmasız değildir. Ethereum, Keccak-256 kullanır (NIST SHA-3’ten biraz farklı bir parameterization).
use sha3::{Sha3_256, Digest};fn main() { let mut hasher = Sha3_256::new(); hasher.update(b"hello world"); let result = hasher.finalize(); println!("{:x}", result);}
SHA-3 vs SHA-2: SHA-2, SHA-NI uzantılarına sahip donanımda daha hızlıdır. HMAC ek yükü olmadan uzunluk uzatma direnci gerektiğinde veya algoritma çeşitliliği argümanında SHA-2’ye ikinci görüntü dirençli alternatif gerektiğinde SHA-3 tercih edilir.
Yazılımda SHA-2’den daha hızlı, SHA-3 ile karşılaştırılabilir güvenlik marjıyla. BLAKE2b 64-bit için; BLAKE2s 32-bit ve gömülü sistemler için optimize edilmiştir. Argon2 tarafından dahili olarak kullanılır.
blake2.ts
import { createHash } from "node:crypto"; // Node 21+ blake2b512 / blake2s256 desteklerconst h = createHash("blake2b512").update("hello").digest("hex");
blake2.rs
use blake2::{Blake2b512, Digest};let mut h = Blake2b512::new();h.update(b"hello world");let result = h.finalize();
SHA değil ama belirtmeye değer — ağaç hashlenebilir, paralel çalışabilen bir hash fonksiyonu. Modern donanımda SHA-256’dan belirgin biçimde daha hızlı.
blake3_hash.rs
use blake3;fn main() { let hash = blake3::hash(b"hello world"); println!("{}", hash.to_hex()); // => "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24"}
BLAKE3 ayrıca anahtarlı hashleme (HMAC yerine) ve anahtar türetmeyi (HKDF yerine) doğal olarak destekler:
blake3-anahtarli.rs
use blake3;// Anahtarlı hashleme — HMAC'ın yerini alırlet key: [u8; 32] = *b"an example very very secret key!";let mac = blake3::keyed_hash(&key, b"mesaj");// Anahtar türetme — HKDF'nin yerini alırlet derived = blake3::derive_key("uygulama bağlamı", b"girdi anahtar materyali");
Tek kullanımlık MAC. Son derece hızlı — ChaCha20-Poly1305’in kimlik doğrulama bileşeni olarak tasarlandı. Anahtar asla yeniden kullanılmamalıdır, bu nedenle genel amaçlı MAC olarak bağımsız kullanılamaz.
Hash tabloları için hızlı, anahtarlı hash — kriptografik anlamda MAC değil. Hash flooding DoS saldırılarını önler. Rust’ın HashMap’i varsayılan olarak SipHash-1-3 kullanır.
siphash.rs
use std::collections::HashMap;// HashMap varsayılan olarak SipHash-1-3 kullanır — bedava gelirlet mut map: HashMap<String, i32> = HashMap::new();
Parola hashlemenin altın standardı. Bellek açısından zorlu, GPU ve ASIC kaba kuvvet saldırılarına dayanıklı. Argon2id; Argon2i (yan kanal dirençli) ve Argon2d’yi (GPU dirençli) birleştirir.
argon2id.ts
import { hash, verify } from "@node-rs/argon2";// Parola hashleme — donanımınıza göre bellek/iterasyonları ayarlayınconst hashed = await hash("kullanici-parolasi", { memoryCost: 65536, // 64 MB timeCost: 3, // 3 iterasyon parallelism: 4, // 4 iş parçacığı algorithm: 2, // argon2id});// Doğrulama — dahili olarak sabit zamanlıconst gecerli = await verify(hashed, "kullanici-parolasi");
Rust’ta — Occlude WASM kripto motoru için argon2 crate’i kullanarak:
Colin Percival’ın bellek açısından zorlu KDF’si. Argon2’den önce. Hâlâ yaygın kullanımda (OpenSSL, libsodium, Ethereum keystore v3). Üç parametre: N (CPU/bellek maliyeti), r (blok boyutu), p (paralellik).
scrypt vs Argon2id: Yeni sistemler için Argon2id tercih edilir. scrypt kabul edilebilir ve yaygın desteklenir. scrypt’in önbellek zamanlama yan kanalları Argon2id’yi kesinlikle daha iyi yapar. Yalnızca mevcut sistemlerle birlikte çalışabilirlik gerektirdiğinde scrypt kullan.
Eski standart. Blowfish tabanlı. Maliyet faktörü her artışta işi iki katına çıkarır. Maksimum girdi: 72 byte (sessizce kırpar — bir tuzak). Bellek açısından zorlu değil. Eski sistemler için hâlâ kabul edilebilir; yeni sistemler için Argon2id’i tercih et.
bcrypt.ts
import bcrypt from "bcrypt";const TUR = 12; // 2^12 iterasyon — modern donanımda ~250msconst hashed = await bcrypt.hash("kullanici-parolasi", TUR);const gecerli = await bcrypt.compare("kullanici-parolasi", hashed);
bcrypt’in 72 byte limiti: Parolalar 72 byte’ı geçebiliyorsa (örn. uzun parolalar), bcrypt’ten önce SHA-256 ile ön hashleme yap — Blowfish’in hatalı ele aldığı null byte içerebilen ham baytlar değil, özetin base64 kodlamasını kullan:
bcrypt-onhashleme.ts
import { createHash } from "node:crypto";import bcrypt from "bcrypt";function onHashle(parola: string): string { return createHash("sha256").update(parola).digest("base64");}const hashed = await bcrypt.hash(onHashle("cok uzun bir parola..."), 12);const gecerli = await bcrypt.compare(onHashle("cok uzun bir parola..."), hashed);
Salt, hash ile birlikte saklanan rastgele, kimlik bilgisi başına üretilen bir değerdir. Salt olmadan özdeş parolalar özdeş hash üretir — toplu saldırıları ve gökkuşağı tablosu aramalarını mümkün kılar.
salt.ts
import { randomBytes } from "node:crypto";// YANLIŞ — salt yok, özdeş parolalar özdeş hash üretirconst bozuk = sha256(parola);// YANLIŞ — statik salt (global salt = uzatılmış parola, gerçek salt değil)const yanlisOlan = sha256("statik-salt" + parola);// DOĞRU — rastgele kimlik bilgisi başına salt, hash ile saklanırconst salt = randomBytes(16).toString("hex");const hash = sha256(salt + parola); // Uygulamada PBKDF2/Argon2 kullanılır// Sakla: { salt, hash }
Argon2/bcrypt/scrypt salt’ları dahili olarak yönetir ve çıktı dizesine kodlar. Bu fonksiyonlarla salt’ları manuel yönetmen gerekmez.
CBC (Şifreli Blok Zincirleme) — her düz metin bloğu şifrelenmeden önce önceki şifreli metin bloğuyla XOR’lanır. PKCS#7 dolgusu ve kimlik doğrulama MAC’ı gerektirir. Yalnızca birlikte çalışabilirlik zorunlu kıldığında kullan.
aes-cbc.ts
import { createCipheriv, createDecipheriv, randomBytes, createHmac, timingSafeEqual } from "node:crypto";// Şifrele-sonra-MAC yapısı — doğru sırafunction cbcSifrele(duzMetin: Buffer, sifreAnahtari: Buffer, macAnahtari: Buffer): { iv: Buffer; sifreliMetin: Buffer; mac: Buffer;} { const iv = randomBytes(16); // CBC için 128-bit IV const sifreleyici = createCipheriv("aes-256-cbc", sifreAnahtari, iv); const sifreliMetin = Buffer.concat([sifreleyici.update(duzMetin), sifreleyici.final()]); // IV + şifreli metin üzerinde MAC (Şifrele-sonra-MAC) const mac = createHmac("sha256", macAnahtari) .update(Buffer.concat([iv, sifreliMetin])) .digest(); return { iv, sifreliMetin, mac };}function cbcCoz(iv: Buffer, sifreliMetin: Buffer, mac: Buffer, sifreAnahtari: Buffer, macAnahtari: Buffer): Buffer { // Çözmeden önce MAC'ı doğrula — dolgu oracle'ı önler const beklenen = createHmac("sha256", macAnahtari) .update(Buffer.concat([iv, sifreliMetin])) .digest(); if (!timingSafeEqual(mac, beklenen)) throw new Error("MAC doğrulaması başarısız"); const cozucu = createDecipheriv("aes-256-cbc", sifreAnahtari, iv); return Buffer.concat([cozucu.update(sifreliMetin), cozucu.final()]);}
Dolgu Oracle Saldırısı: Bir şifre çözme oracle’ı dolgunun geçerli olup olmadığını açıklarsa (farklı hata mesajları, zamanlama farkları veya davranış yoluyla bile), saldırgan anahtarı bilmeden herhangi bir CBC şifreli metni byte byte çözebilir. Her zaman çözmeden önce MAC’ı doğrula. Yeni kodda AES-CBC yerine her zaman AES-GCM kullan.
Sayaç modu — AES’i akış şifresine dönüştürür. Aynı anahtar için nonce/sayaç kombinasyonu asla tekrarlanmamalıdır. Dolgu yok. Kimlik doğrulamalı değil — HMAC ile birleştirilmelidir.
aes-ctr.ts
import { createCipheriv, randomBytes } from "node:crypto";// CTR şifreleme ve çözme için aynı fonksiyonu kullanırfunction aesCtr(veri: Buffer, anahtar: Buffer, sayac: Buffer): Buffer { const sifreleyici = createCipheriv("aes-256-ctr", anahtar, sayac); return Buffer.concat([sifreleyici.update(veri), sifreleyici.final()]);}const anahtar = randomBytes(32);const sayac = randomBytes(16); // 128-bit sayaç bloğuconst sifreliMetin = aesCtr(Buffer.from("duz metin"), anahtar, sayac);const duzMetin = aesCtr(sifreliMetin, anahtar, sayac); // aynı işlem
AES-GCM’in modern alternatifi. Donanım hızlandırma gerektirmez (AES-NI’nin aksine), tasarım gereği sabit zamanlı ve her mimaride zamanlama saldırılarına karşı dayanıklıdır. TLS 1.3, WireGuard ve SSH’de kullanılır.
Uzatılmış nonce varyantı — 96-bit yerine 192-bit nonce. Nonce çarpışması riskini tamamen ortadan kaldırır, bu da sayaç olmadan rastgele nonce üretmeyi güvenli kılar. Occlude’un kullandığı budur.
AEAD şifreler hem şifreli metni hem de ek düz metin meta verilerini doğrular. AAD şifrelenmez ama şifreli metne bağlanır — AAD’yi değiştirmek şifre çözmeyi başarısız kılar.
aead-aad.ts
// AAD ile AES-GCM — AAD doğrulanır ama şifrelenmezasync function aadIleSimrele( duzMetin: string, anahtar: CryptoKey, aad: Uint8Array // örn. kayıt ID, kullanıcı ID, şema versiyonu): Promise<{ nonce: Uint8Array; sifreliMetin: Uint8Array }> { const nonce = crypto.getRandomValues(new Uint8Array(12)); const kodlanmis = new TextEncoder().encode(duzMetin); const sifreliMetin = new Uint8Array( await crypto.subtle.encrypt( { name: "AES-GCM", iv: nonce, additionalData: aad }, anahtar, kodlanmis ) ); return { nonce, sifreliMetin };}// Kullanım: şifreli metni kayıt ID'sine bağla — kesip yapıştırma saldırılarını önlerconst kayitId = new TextEncoder().encode("kayit-uuid-1234");const { nonce, sifreliMetin } = await aadIleSimrele(gizli, anahtar, kayitId);// Saldırgan bu şifreli metni farklı bir kayıt ID'si altına yapıştıramaz — şifre çözme başarısız olur
Yaygın AAD adayları: kayıt/satır ID’si, kullanıcı ID’si, şema versiyonu, protokol versiyonu, zaman damgası (kaba).
Dijital imzalar için kullanılır. İmzalayan özel anahtara sahiptir; herkes açık anahtarla doğrulayabilir. secp256k1 Bitcoin/Ethereum tarafından kullanılır; P-256 (secp256r1) NIST standardıdır.
Edwards eğrisi Dijital İmza Algoritması. ECDSA’dan daha hızlı, deterministik (imza başına rastgele nonce gerekmez) ve yan kanal saldırılarına dayanıklı.
ed25519.ts
import { ed25519 } from "@noble/curves/ed25519";// Anahtar üretimiconst ozelAnahtar = ed25519.utils.randomPrivateKey();const acikAnahtar = ed25519.getPublicKey(ozelAnahtar);// İmzalaconst mesaj = new TextEncoder().encode("bu isteği doğrula");const imza = ed25519.sign(mesaj, ozelAnahtar);// Doğrulaconst gecerli = ed25519.verify(imza, mesaj, acikAnahtar);console.log(gecerli); // => true
Rust’ta:
ed25519_imza.rs
use ed25519_dalek::{SigningKey, Signer, Verifier};use rand::rngs::OsRng;fn main() { let imzalama_anahtari = SigningKey::generate(&mut OsRng); let dogrulama_anahtari = imzalama_anahtari.verifying_key(); let mesaj = b"bu isteği doğrula"; let imza = imzalama_anahtari.sign(mesaj); assert!(dogrulama_anahtari.verify(mesaj, &imza).is_ok());}
ECDSA vs Ed25519:
ECDSA imza başına rastgele nonce gerektirir — bozuk bir RNG, özel anahtarı sızdıran bozuk bir imza üretir. PS3 bu şekilde kırıldı.
Ed25519 deterministiktir — aynı anahtar + aynı mesaj = aynı imza. RNG bağımlılığı yok.
Curve25519 üzerinde Diffie-Hellman anahtar anlaşması. İki taraf, onu asla iletmeden aynı paylaşılan gizliyi türetir. Paylaşılan gizli ardından şifreleme anahtarları üretmek için HKDF’ye beslenir.
x25519.ts
import { x25519 } from "@noble/curves/ed25519";// Alice anahtar çiftini üretirconst aliceOzel = x25519.utils.randomPrivateKey();const aliceAcik = x25519.getPublicKey(aliceOzel);// Bob anahtar çiftini üretirconst bobOzel = x25519.utils.randomPrivateKey();const bobAcik = x25519.getPublicKey(bobOzel);// Her ikisi de aynı paylaşılan gizliyi türetirconst alicePaylasilan = x25519.getSharedSecret(aliceOzel, bobAcik);const bobPaylasilan = x25519.getSharedSecret(bobOzel, aliceAcik);// alicePaylasilan === bobPaylasilan (aynı 32 byte)// Şifreleme anahtarları türetmek için HKDF'ye besle
Klasik. Hâlâ anahtar sarmalama ve eski sistemler için kullanılır. ECC’ye kıyasla daha büyük anahtarlar (eşdeğer güvenlik için 256 bit yerine 2048-4096 bit).
RSA-OAEP vs RSA-PKCS1v1.5: PKCS1v1.5, BLEICHENBACHER saldırısına (uyarlanabilir seçilmiş şifreli metin) karşı savunmasızdır. Yeni şifreleme için asla kullanma. RSA-OAEP doğru dolgudur.
RSA imzaları için doğru dolgu. RSA-PKCS1v1.5 imzaları deterministik ve potansiyel olarak değiştirilebilirdir; PSS rastgelelik ve güvenlik kanıtı ekler.
rsa-pss.ts
const anahtarCifti = await crypto.subtle.generateKey( { name: "RSA-PSS", modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256", }, true, ["sign", "verify"]);const imza = await crypto.subtle.sign( { name: "RSA-PSS", saltLength: 32 }, // saltLength = hash çıktı uzunluğu anahtarCifti.privateKey, new TextEncoder().encode("imzalanacak mesaj"));const gecerli = await crypto.subtle.verify( { name: "RSA-PSS", saltLength: 32 }, anahtarCifti.publicKey, imza, new TextEncoder().encode("imzalanacak mesaj"));
RSA yalnızca küçük miktarda veriyi şifreleyebilir (anahtar boyutuyla sınırlı). Asimetrik kripto yavaştır. Çözüm: rastgele simetrik anahtarı asimetrik kripto ile şifrele, ardından gerçek veriyi bu simetrik anahtarla şifrele. Bu hibrit şifreleme — TLS, PGP ve gerçek dünyadan her E2EE sisteminin temelidir.
Geçici anahtar çifti ileriye dönük gizlilik sağlar — alıcının uzun vadeli özel anahtarı daha sonra ele geçirilse bile, her şifreleme farklı bir geçici anahtar kullandığı için geçmiş mesajlar güvende kalır.
k / n şeması: geçerli bir imza üretmek için n taraftan k’sının işbirliği gerekir. Hiçbir tek taraf tam özel anahtarı tutmaz. Çoklu imza cüzdanları ve HSM kümelerinde kullanılır.
ECDSA’dan daha basit, rastgele oracle modelinde kanıtlanmış güvenli ve imza agregasyonunu destekler (birden fazla imza tek birine birleşir). Bitcoin Taproot Schnorr kullanır.
İki taraf açık kanal üzerinde paylaşılan gizli konusunda anlaşır. Hiçbiri gizliyi iletmez. Ayrık logaritma problemi bir dinleyicinin gizliyi hesaplamasını engeller.
Klasik DH, asal modulo çarpımsal gruplar kullanır. Modern sistemler eliptik eğriler kullanır (ECDH) — eşdeğer güvenlik için çok daha küçük anahtarlar.
DH kimlik doğrulamasızdır — ek kimlik doğrulama katmanı olmadan (imzalar, PKI, önceden paylaşılmış anahtarlar) ortadaki adam saldırısına açıktır.
Güvenli, kimlik doğrulamalı ve isteğe bağlı olarak ileriye dönük gizli el sıkışmalar oluşturmak için modern çerçeve. WireGuard Noise_IKpsk2 desenini kullanır. Her desen hangi anahtarların ne zaman iletileceğini açıklayan token dizisiyle (örn. XX, IK, NX) tanımlanır.
Uçtan uca şifreli mesajlaşmanın altın standardı. İkisini birleştirir:
X3DH (Genişletilmiş Üçlü Diffie-Hellman) başlangıç anahtar anlaşması için
Çift Mandal devam eden mesaj şifreleme için — bir Diffie-Hellman mandalının (mola kurtarma sağlar) ve simetrik anahtar mandalın (mesaj başına ileriye dönük gizlilik sağlar) kombinasyonu
Her mesaj, mandalı ilerletmekle türetilen farklı bir anahtar kullanır. Bir mesaj anahtarının ele geçirilmesi geçmiş veya gelecek mesajları tehlikeye atmaz.
Yükü şifreler. Tam akış: rastgele İçerik Şifreleme Anahtarı (CEK) üret, AES-GCM veya ChaCha20-Poly1305 ile CEK kullanarak yükü şifrele, RSA-OAEP veya ECDH-ES kullanarak alıcının açık anahtarıyla CEK’i şifrele.
import { createHmac, randomBytes } from "node:crypto";function totp(gizli: Buffer, pencere = 0): string { const zaman = Math.floor(Date.now() / 1000 / 30) + pencere; return hotp(gizli, zaman);}function totpDogrula(gizli: Buffer, kod: string, kayma = 1): boolean { // Saat kayması için ±kayma pencerelerini kontrol et for (let p = -kayma; p <= kayma; p++) { if (totp(gizli, p) === kod) return true; } return false;}// Kimlik doğrulama uygulamaları için gizli ve QR URI üretfunction totpGizliOlustur(): { gizli: string; uri: string } { const gizli = randomBytes(20).toString("base32"); const uri = `otpauth://totp/UygulamAdi:kullanici@ornek.com?secret=${gizli}&issuer=UygulamAdi&algorithm=SHA1&digits=6&period=30`; return { gizli, uri };}
Üretimde TOTP: Kendin yazmak yerine otplib veya @oslojs/otp kütüphanesini kullan. Her zaman hız sınırlaması ve kilitleme uygula — TOTP yalnızca 6 hanedir.
RFC 2945. Parolayı veya parola hash’ini sunucuya hiçbir zaman iletmeyen parola kimlik doğrulama protokolü. DH tabanlı. Sunucu ele geçirilse bile saldırgan parolayı öğrenemez.
İstemci:A = g^a mod N (sunucuya gönder)
Sunucu:B = kv + g^b mod N (v sunucuda saklanan parola doğrulayıcısı)
Her ikisi hesaplar:S = (DH'dan paylaşılan gizli)
Her ikisi türetir:K = H(S), M1 = H(A, B, K), M2 = H(A, M1, K)
İstemci M1 gönderir; Sunucu M2 gönderir — K'nın karşılıklı kanıtı
SRP doğru uygulamak karmaşıktır — TypeScript için tssrp6a, Rust için srp crate kullan.
Her yaprak düğümün bir veri bloğunun hash’i ve her yaprak olmayan düğümün çocuklarının hash’i olduğu ikili ağaç. Kök hash tüm veri kümesini taahhüt eder. Git, Bitcoin, Ethereum, sertifika şeffaflığı ve Tailscale’de kullanılır.
merkle.ts
import { createHash } from "node:crypto";function sha256(veri: Buffer): Buffer { return createHash("sha256").update(veri).digest();}function merkleKoku(yapraklar: Buffer[]): Buffer { if (yapraklar.length === 0) throw new Error("bos"); if (yapraklar.length === 1) return yapraklar[0]; const sonraki: Buffer[] = []; for (let i = 0; i < yapraklar.length; i += 2) { const sol = yapraklar[i]; const sag = yapraklar[i + 1] ?? sol; // tek sayıysa sonuncuyu çoğalt sonraki.push(sha256(Buffer.concat([sol, sag]))); } return merkleKoku(sonraki);}function merkleKaniti(yapraklar: Buffer[], index: number): Buffer[] { const kanit: Buffer[] = []; let guncel = yapraklar; let idx = index; while (guncel.length > 1) { const sonraki: Buffer[] = []; for (let i = 0; i < guncel.length; i += 2) { const sol = guncel[i]; const sag = guncel[i + 1] ?? sol; if (i === idx || i + 1 === idx) { kanit.push(idx % 2 === 0 ? sag : sol); } sonraki.push(sha256(Buffer.concat([sol, sag]))); } idx = Math.floor(idx / 2); guncel = sonraki; } return kanit;}
H(H(H(...H(tohum)...))) dizisi. Bir kimlik bilgisini iptal etmek N konumundaki ön görüntüyü açıklamak anlamına gelir; sonraki konumlar iptal edilemez. S/KEY (OTP), Lamport saatleri ve yalnızca ekleme destekli günlüklerde kullanılır.
AWS KMS, Google Cloud KMS ve HashiCorp Vault tarafından kullanılan desen. Veri Şifreleme Anahtarı (DEK) veriyi şifreler; DEK’in kendisi Anahtar Şifreleme Anahtarı (KEK) ile şifrelenir. KEK donanımda (HSM/KMS) yaşar ve asla oradan çıkmaz.
zarf.ts
import { randomBytes } from "node:crypto";interface ZarfSifrelenmis { sifreliDek: Buffer; // KEK ile şifrelenmiş DEK — veriyle birlikte saklamak güvenli nonce: Buffer; sifreliMetin: Buffer; etiket: Buffer;}async function zarfSifrele( duzMetin: Buffer, kek: Buffer // Anahtar Şifreleme Anahtarı — KMS/HSM'de yaşar): Promise<ZarfSifrelenmis> { // 1. Bu kayıt için yeni DEK üret const dek = randomBytes(32); // 2. Veriyi DEK ile şifrele const { nonce, sifreliMetin, etiket } = aesGcmSifrele(duzMetin, dek); // 3. DEK'i KEK ile şifrele (anahtar sarmalama) const sarılmısDek = aesGcmSifrele(dek, kek); // 4. Şifreli metnin yanında sifreliDek'i sakla // Ham DEK kullanımın ardından bellekten silinir dek.fill(0); // sıfırla return { sifreliDek: Buffer.concat([sarılmısDek.nonce, sarılmısDek.etiket, sarılmısDek.sifreliMetin]), nonce, sifreliMetin, etiket };}
HSM, özel anahtarları üretip saklayan kurcalamaya dayanıklı donanım aygıtıdır. Anahtarlar HSM’den hiçbir zaman şifresiz çıkmaz. İşlemler (imzala, çöz) HSM içinde gerçekleşir. CA kök anahtarları, ödeme işleme (PCI-DSS) ve devlet sınıfı anahtar yönetimi için kullanılır.
Bulut eşdeğerleri: AWS CloudHSM, Google Cloud HSM, Azure Dedicated HSM. Daha basit KMS API’leri: AWS KMS, GCP KMS (anahtarlar HSM destekli veya yazılım tabanlı olabilir).
CPU yürütme süresi bilgi sızdırır. Bir karşılaştırma ilk farklı byte’ta erken çıkarsa, zamanlama ölçümleri doğru değeri byte byte ortaya çıkarır. Bu, zamanlama yan kanal saldırılarının temelidir.
sabit-zamanli.ts
import { timingSafeEqual } from "node:crypto";// YANLIŞ — ilk farklılıkta kısa devre yaparfunction kotüKarsilastir(a: string, b: string): boolean { return a === b; // kaç byte'ın eşleştiğini sızdırır}// DOĞRU — farkın nerede olduğundan bağımsız olarak her zaman tüm byte'ları karşılaştırırfunction guvenliKarsilastir(a: Buffer, b: Buffer): boolean { if (a.length !== b.length) { // Uzunluk kontrolü önceden yapılabilir — saldırgan beklenen uzunluğu zaten biliyor return false; } return timingSafeEqual(a, b);}// Uygulamada:function hmacDogrula(beklenen: string, alinan: string): boolean { const a = Buffer.from(beklenen, "hex"); const b = Buffer.from(alinan, "hex"); if (a.length !== b.length) return false; return timingSafeEqual(a, b);}
sabit-zamanli.rs
use subtle::ConstantTimeEq;fn mac_dogrula(beklenen: &[u8], alinan: &[u8]) -> bool { beklenen.ct_eq(alinan).into()}
ChaCha20 tüm mimarilerde sabit zamanlı olacak şekilde tasarlanmıştır — tablo araması yok, veriye bağlı dal yok. AES, sabit zamanlı olmak için AES-NI donanım talimatları gerektirir; bunlar olmadan AES tablo aramaları, önbellek zamanlaması yoluyla anahtar bit’lerini sızdırır (AES önbellek zamanlama saldırısı, 2005).
ChaCha20’nin AES-NI olmayan cihazlarda (eski ARM, IoT donanımı) neden tercih edildiğinin nedeni budur.
Bir sistem dolgunun geçerli olup olmadığını açıklarsa (farklı hata mesajları, yanıt süreleri veya davranış yoluyla), saldırgan uyarlanabilir seçilmiş şifreli metin sorguları kullanarak herhangi bir CBC şifreli metni byte byte çözebilir. Çözüm: AEAD kullan (AES-GCM, ChaCha20-Poly1305).
AES-GCM ve ChaCha20-Poly1305’te: bir (anahtar, nonce) çiftini yeniden kullanmak iki düz metnin XOR’unu açığa çıkarır ve kimlik doğrulamayı bozar. ECDSA’da: k rastgele nonce’unu yeniden kullanmak özel anahtarı açığa çıkarır (PS3 hack’i, 2010). Azaltma: AES-GCM için sayaçlar, deterministik imzalar (Ed25519), rastgele nonce’lar için XChaCha20.
Saldırgan geçerli bir kimlik doğrulamalı mesajı yakalar ve yeniden iletir. Azaltma: kimlik doğrulamalı yüke zaman damgası ve/veya nonce ekle, zaman penceresi dışındaki veya görülen nonce’lara sahip mesajları reddet.
CSPRNG düşük entropili verilerle (zaman, PID) tohumlanırsa anahtarlar tahmin edilebilir. Azaltma: her zaman OS destekli entropi kullan. Asla yalnızca Date.now()’dan tohum alma.
AES-GCM anahtara taahhüt etmez — (çabayla) iki farklı anahtar altında çözülen bir şifreli metin oluşturmak mümkündür. Bu, çok alıcılı protokolleri bozar. AES-GCM-SIV kullan veya açık anahtar taahhüdü ekle. Bu aktif bir araştırma alanıdır (2023+); dağıtımdaki çoğu sistem tek alıcı kullandığından etkilenmez.