GPG'ye Modern ve Basit Bir Alternatif: age ve Arkasındaki Kriptografik Mühendislik
🇹🇷 GPG'nin karmaşık yapısına modern bir alternatif olan 'age' dosya şifreleme aracının kaynak kodunu inceleyerek, arkasındaki kriptografik mühendisliği, kod mimarisini ve post-kuantum yeteneklerini ele alıyoruz.
Yıllar boyunca dosya şifreleme ve imzalama dendiğinde akla gelen ilk (ve genellikle tek) araç GPG (GnuPG) oldu. Ancak GPG’nin karmaşık yapılandırma seçenekleri, hantal anahtar yönetimi (keyring) ve esnemiş kod tabanı, modern kullanım senaryolarında sıkça eleştiriliyor.
İşte tam bu noktada, Filippo Valsorda tarafından tasarlanan age devreye giriyor. age, “küçük, açık anahtarlar, yapılandırma seçeneği olmayan ve UNIX felsefesine uygun” bir dosya şifreleme aracı ve formatıdır.
Bu yazıda, age deposunun kaynak kodlarına inerek, bu aracın arkasında yatan zarif kriptografik mühendisliği, kod mimarisini ve post-kuantum (PQ) şifreleme yeteneklerini inceleyeceğiz.
GPG ile Kısa Bir Karşılaştırma
Neden yeni bir araca ihtiyaç duyuldu? İşte somut fark:
GPG ile bir dosyayı şifrelemek:
1
2
3
4
5
6
gpg --gen-key # Etkileşimli anahtar oluşturma sihirbazı
gpg --keyserver keys.openpgp.org \
--search-keys alici@ornek.com # Anahtar sunucusunda arama
gpg --fingerprint alici@ornek.com # Parmak izi doğrulama
gpg --sign-key alici@ornek.com # Güven zinciri imzalama
gpg --encrypt -r alici@ornek.com dosya.txt # Şifreleme
age ile aynı işlem:
1
2
3
age-keygen -o anahtar.txt # Anahtar oluşturma
age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j... \
dosya.txt > dosya.age # Şifreleme
GPG’de Web of Trust, keyring yönetimi ve anahtar sunucusu karmaşası varken; age‘de tüm bunlar yoktur — kasıtlı olarak.
Temel Tasarım Felsefesi
age‘in kaynak kodunu (özellikle cmd/age ve age.go dosyalarını) incelediğimizde, tasarımın şu temellere dayandığını görüyoruz:
- Global Keyring Yok: GPG’nin aksine
agearka planda gizli bir veritabanı tutmaz. Anahtarlar tıpkı SSH anahtarları gibi basit metin dosyalarıdır. - Tek İş, Tam İş: Araç sadece şifreleme ve deşifreleme yapar. İmzalama (signing) işlevi bilinçli olarak kapsama alınmamıştır (bunun için
minisignveyasshkullanılabilir). - Malleability (Değiştirilebilirlik) Koruması: Şifrelenmiş dosyalar AEAD (Authenticated Encryption with Associated Data) ile korunur. Dosyanın tek bir biti bile değişse, deşifreleme işlemi hata verir.
Hızlı Başlangıç
1
2
3
4
5
6
7
8
9
10
11
12
# Anahtar çifti oluşturma
age-keygen -o ~/.age/anahtar.txt
# Çıktı: Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j...
# Şifreleme
age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j... gizli.txt > gizli.age
# Deşifreleme
age -d -i ~/.age/anahtar.txt gizli.age > gizli.txt
# Parola ile şifreleme (asimetrik anahtar olmadan)
age -p gizli.txt > gizli.age
Mimari: Dosya Formatı ve Şifreleme Süreci
Bir dosya age ile şifrelendiğinde, işlem iki ana aşamada gerçekleşir: Başlık (Header) oluşturma ve Yük (Payload) şifreleme.
1. File Key (Dosya Anahtarı)
Her şifreleme işleminde age rastgele 16 byte’lık (128-bit) bir File Key üretir. 128-bit, klasik bilgisayarlara karşı 2^128 brute-force direnci sağlar. File Key doğrudan şifreleme anahtarı olarak kullanılmaz; HKDF ile genişletildiği için asıl şifreleme 256-bit Stream Key ile yapılır. Bu nedenle 128-bit File Key yeterlidir.
Bu 16 byte’lık File Key, daha sonra HKDF algoritmasıyla 32 byte’lık (256-bit) bir Stream Key’e dönüştürülür ve asıl yük (payload) bu türetilmiş anahtarla şifrelenir.
Dosyayı okuması istenen her alıcı (recipient) için ise orijinal 16 byte’lık File Key, alıcının açık anahtarı ile şifrelenerek başlık (header) kısmına eklenir.
graph TD
A[Orijinal Dosya] --> STREAM[STREAM Şifreleme - ChaCha20Poly1305]
B[16-byte Rastgele File Key] --> STREAM
STREAM --> C[Şifrelenmiş Yük / Payload]
B --> W1[Alıcı 1 için Şifrele - X25519]
B --> W2[Alıcı 2 için Şifrele - SSH-RSA]
B --> W3[Alıcı 3 için Şifrele - Post-Quantum]
W1 --> H[Header / Başlık]
W2 --> H
W3 --> H
H --> F[Final .age Dosyası]
C --> F
2. Header (Başlık) ve Stanza Yapısı
internal/format paketini incelediğimizde, başlığın saf metin (ASCII) tabanlı olduğunu görüyoruz. Başlık her zaman age-encryption.org/v1 versiyon bilgisiyle başlar.
Her alıcı için başlığa bir Stanza (kıta/bölüm) eklenir. Örneğin bir X25519 alıcısı için Stanza şu şekildedir:
-> X25519 [Ephemeral_Public_Key] ve altında Base64 ile kodlanmış, şifreli File Key yer alır.
Başlık, tüm stanza’ların sonuna eklenen bir HMAC (Header MAC, spesifik olarak HMAC-SHA256) ile bütünlük kontrolüne tabi tutulur. Bu sayede saldırganlar başlığa sahte alıcılar ekleyemez veya çıkaramaz.
3. Payload ve STREAM Şifrelemesi
Gerçek mühendislik harikalarından biri internal/stream/stream.go dosyasında yatıyor. age, belleği (RAM) tüketmemek için devasa dosyaları tek seferde şifrelemek yerine STREAM adı verilen bir parçalama (chunking) mekanizması kullanır.
- Dosya 64 KB’lık parçalara (chunks) bölünür.
- Şifreleme algoritması olarak ChaCha20-Poly1305 kullanılır. AES-GCM’e kıyasla donanım hızlandırması (AES-NI) olmayan sistemlerde daha hızlı çalışır; ayrıca cache-timing yan kanal saldırılarına karşı doğası gereği dayanıklıdır.
- HKDF-SHA256 kullanılarak File Key ve rastgele bir nonce’dan Stream Key türetilir.
- Her parçanın nonce değeri, parça sırasına göre artırılır. Bu, parçaların yerinin değiştirilmesini veya aradan parça silinmesini engeller.
- Son parçanın nonce’unun son byte’ı
0x01olarak işaretlenir. Böylece dosyanın kırpılması (truncation attack) anında tespit edilir.
sequenceDiagram
participant Plaintext
participant Chunk as 64KB Chunk
participant Cipher as ChaCha20-Poly1305
participant Output as Şifreli Dosya
Plaintext->>Chunk: Veri Akışı
loop Her 64 KB için
Chunk->>Cipher: Veri + StreamKey + Nonce (Sayaç: N)
Cipher->>Output: Şifreli Veri + Poly1305 MAC
end
Note over Cipher: Son chunk'ta Nonce'un<br/>son byte'ı 0x01 yapılır.
Desteklenen Anahtar ve Alıcı Türleri
Kod tabanında Recipient ve Identity interfaceleri kullanılarak polimorfik bir yapı kurulmuştur.
A. X25519 (Varsayılan Modern Anahtar)
Standart age anahtarlarıdır. Kodda x25519.go içerisinde yer alır. İnsanların anahtarları yanlış kopyalamasını veya eksik yazmasını engellemek için Bitcoin’den tanıdığımız Bech32 formatını (internal/bech32) kullanır.
- Açık Anahtar:
age1...ile başlar. - Gizli Anahtar:
AGE-SECRET-KEY-1...ile başlar.
B. SSH Anahtarları Uyumluluğu
Ekosisteme entegrasyonu kolaylaştırmak için agessh/ paketi, mevcut ssh-rsa ve ssh-ed25519 anahtarlarıyla şifreleme yapmaya olanak tanır. GitHub üzerindeki bir kullanıcının anahtarlarına şifreleme yapmak şu kadar basittir:
1
2
curl -o torvalds.keys https://github.com/torvalds.keys
age -R torvalds.keys gizli_belge.txt > belge.age
C. Scrypt (Parola Koruması)
Eğer asimetrik bir anahtar kullanmak istemiyorsanız, age -p ile parola tabanlı şifreleme yapabilirsiniz. scrypt.go dosyası, parolayı brute-force saldırılarına karşı yavaşlatmak (work factor) için golang.org/x/crypto/scrypt algoritmasını kullanır.
D. Post-Kuantum Şifreleme (Hybrid ML-KEM-768 + X25519)
age‘in heyecan verici özelliklerinden biri, kuantum bilgisayarların gelecekteki “şimdi topla, sonra çöz” (harvest now, decrypt later) saldırılarına karşı veriyi korumak için NIST standardı olan ML-KEM-768 ile X25519‘u birleştiren hibrit yapıdır. Bu özellik pq.go dosyasında yer almakta olup age-plugin-pq gibi eklenti sistemi aracılığıyla ya da doğrudan ana kütüphane üzerinden kullanılabilmektedir.
Not: PQ desteğinin stabil release’e dahil edilme durumunu ve tam versiyon bilgisini kullanmadan önce resmi GitHub deposunu kontrol etmenizi öneririz; bu alan aktif geliştirme altındadır. PQ açık anahtarları
age1pq1...ile başlar (yaklaşık 2000 karakter uzunluğundadır).
Eklenti (Plugin) Sistemi ve YubiKey Entegrasyonu
plugin/ klasörü, age‘in UNIX borularını (pipes) ve stdin/stdout akışlarını kullanarak nasıl genişletilebilir olduğunu gösterir.
Eğer age dosyayı deşifre ederken başlıkta -> yubikey-piv gibi yerleşik olarak desteklemediği bir stanza ismi (stanza name) okursa, $PATH üzerinde age-plugin-yubikey-piv adında bir binary (çalıştırılabilir dosya) arar. Bulduğunda, bu alt süreci (subprocess) başlatır ve şifreli File Key’i bu araca stdout üzerinden göndererek deşifre edip geri dönmesini bekler.
Bu mimari sayesinde, donanım token’ları (YubiKey, Trezor), donanım güvenlik modülleri (KMS) veya bulut sağlayıcıları için ana age kod tabanını şişirmeden bağımsız eklentiler geliştirilebilmektedir.
Ekosistem: rage (Rust Implementasyonu)
Go ile yazılmış orijinal age‘in yanı sıra, rage adında bir Rust implementasyonu da aktif olarak geliştirilmektedir. Format ve protokol açısından tam uyumlu olan rage, Rust’ın bellek güvenliği garantilerini araç zincirine dahil etmek isteyen geliştiriciler için iyi bir alternatiftir. Her iki implementasyon da .age dosyalarını sorunsuz okuyup yazabilir.
Sonuç
age, kod kalitesi, minimalizmi ve güvenli varsayılanları (secure defaults) ile GPG’nin hantallığına kusursuz bir yanıttır. Go dilinin gücünü ve modern kriptografi standartlarını (ChaCha20-Poly1305, X25519, ML-KEM) arkasına alarak güvenlik araştırmacılarının, yazılım geliştiricilerin ve sistem yöneticilerinin alet çantasında kesinlikle bulunması gereken bir araç haline gelmiştir.
Kod tabanının detaylı analizini okumak isterseniz GitHub deposuna göz atabilirsiniz.
