İçindekiler
- Eşleştirme Neden Var
- Güven Modeli ve Kriptografi
- Bileşen Haritası
- Keşif Aşaması
- Eşleştirme Sırası (Mutlu Yol)
- Anahtar Değişimi Detayları
- Yanıtlayıcı Kabul/Red Akışı
- İptal Akışı
- Çift Kanal Teslimi (LAN + BLE)
- Oturum ve Eş Depolama
- Güvenlik Özellikleri
- Durum Makinesi Özeti
Eşleştirme Neden Var {#why-pairing-exists}
PlainApp'in merkezi bir hesap sunucusu yoktur. Bu nedenle cihazlar, birbirleriyle konuşmadan önce iki soruyu yanıtlamalıdır:
- "Kimsin?" — Her cihaz, ilk açılışta sabit bir
clientIdoluşturur (Ed25519 anahtar materyalinden türetilmiş 13 karakterlik bir kimlik). Bu, yönlendirme, varlık ve kanal üyeliği için kullanılan tek tanımlayıcıdır. - "Sana güvenebilir miyim?" — Bir sunucunun kimliği garanti etmediği durumlarda, bir eşin iddia ettiği kişi olduğundan emin olmanın tek yolu, bir insanın eşleştirmeyi her iki cihazda da onaylaması ve protokolün kriptografik imzaları doğrulamasıdır.
Eşleştirme tek bir yapıt üretir: veritabanında status="paired", bir
ChaCha20 key (paylaşılan taşıma sırrı) ve eşin Ed25519 public_key
(gelecekteki mesaj imzalarını doğrulamak için) ile bir DPeer satırı.
Sohbet alt sistemindeki sonraki her protokol, bu iki alanın varlığını varsayar.
Güven Modeli ve Kriptografi {#trust-model--cryptography}
Eşleştirme, iki bağımsız kriptografik ilkel kullanır:
| İlkel | Amaç | Yaşam Döngüsü |
|---|---|---|
| Ed25519 (imza) | Eşleştirme isteğini ve yanıtını doğrulamak. «Bu gerçekten gönderdiğini iddia eden cihazdan geldi» doğrular ve tekrar saldırılarını önlemek için zaman damgasını bağlar. | İmzalama anahtarı, cihazın uzun vadeli kimlik anahtarıdır. Açık yarısı DPeer.public_key olarak saklanır ve daha sonra her sohbet mesajı imzasını doğrulamak için PeerChatParser.decrypt tarafından kullanılır. |
| X25519 tarzı ECDH (anahtar anlaşması) | ChaCha20 taşıma anahtarı haline gelen paylaşılan bir sır üretmek. İki cihaz, sırrı hiçbir zaman iletmeden aynı sırrı hesaplar. | Eşleştirme oturumu başına kısa ömürlü anahtar çifti üretilir, paylaşılan anahtar hesaplandıktan hemen sonra atılır. Elde edilen 32 baytlık sır DPeer.key olarak saklanır ve eşleştirmenin ömrü boyunca yeniden kullanılır. |
PIN, QR kodu, bant dışı kod yoktur. Güven şu şekilde kurulur:
- Bir insanın yanıtlayıcı cihazda Kabul'e dokunması (kullanıcı «evet, bu benim eşleştirmek istediğim cihaz» iddia ediyor).
- Her iki tarafın istek/yanıt üzerinde diğerinin Ed25519 imzasını doğrulaması (yanıtlayıcının oturumu başlatan cihazla konuştuğunu ve tersini kanıtlar).
- Her iki mesajda ±5 dakikalık zaman damgası penceresi (yakalanmış eski bir el sıkışmanın tekrarını önler).
Bu asimetri önemlidir: Tek bir insan onayı, ortadaki adam saldırısına karşı savunmasız olur (saldırgan her iki tarafla ayrı ayrı eşleşebilir). ECDH ortak anahtarı üzerindeki Ed25519 imzası bunu önler — yanıtlayıcı, isteğin oturumu başlatan aynı Ed25519 anahtarı tarafından imzalandığını doğrular ve tersi, böylece bir MITM, uzun vadeli imzalama anahtarını da kontrol etmeden kendi ECDH anahtarını şeffaf bir şekilde değiştiremez.
Bileşen Haritası {#component-map}
Tüm eşleştirme kodu discover/ paketinde bulunur (chat/peer/pair/ değil):
Dosya konumları
| Bileşen | Yol (shared/src/commonMain/kotlin/com/ismartcoding/plain/ altında) |
|---|---|
LANDiscoverManager | discover/LANDiscoverManager.kt |
PairingCore | discover/PairingCore.kt |
PairingInitiator | discover/PairingInitiator.kt |
PairingResponder | discover/PairingResponder.kt |
PairingSecurity | discover/PairingSecurity.kt |
PairingSessionStore | discover/PairingSessionStore.kt |
PairingPeerStore | discover/PairingPeerStore.kt |
PairingMessenger | discover/PairingMessenger.kt |
Keşif Aşaması {#discovery-phase}
Eşleştirme gerçekleşmeden önce cihazlar birbirlerini bulmalıdır.
LANDiscoverManager, uygulama başladığında sürekli çalışır:
Yönlendirilmiş keşif neden şifrelidir
Yayın DISCOVER'ı hassas bir şey ortaya çıkarmaz (sadece fromId=clientId),
bu nedenle LAN'daki herhangi bir cihazın bunu görmesi sorun değildir. Ancak
yönlendirilmiş varyant, bir cihaz zaten başka bir cihazın clientId'sini
bildiğinde (örneğin eşleştirilmiş ama eşin IP'si değişti) ve onu uyandırmak
istediğinde kullanılır. Hedef clientId'yi eşin paylaşılan anahtarıyla
şifrelemek şu anlama gelir:
- Doğru eş
toId'nin şifresini çözebilir, kendini tanır ve yanıt verebilir. - LAN'daki diğer tüm cihazlar yalnızca şifreli metni görür — gönderenin
hangi
clientId'lere ulaşmaya çalıştığını sayamazlar.
Bu küçük ama gerçek bir gizlilik özelliğidir: Pasif LAN gözlemcileri, kimin kiminle eşleştirildiğinin grafiğini çıkaramaz.
Yanıttaki Aware bayrakları
DISCOVER_REPLY, awareSupported ve awareRunning taşır. Bunlar veritabanına
kalıcı olarak kaydedilmez — PeerCacher'da bellekte saklanır ve her
yanıtta (ve BLE tarama-yanıtı serviceData'sından) yenilenir. Taşıma katmanı,
bir Wi-Fi Aware bağlantısı denemeye mi yoksa doğrudan BLE'ye mi geçmeye karar
verirken bunlara danışır.
Eşleştirme Sırası (Mutlu Yol) {#pairing-sequence-happy-path}
Her iki cihaz da aynı LAN'da olduğunda ve kullanıcı eşleştirmeyi kabul ettiğinde uçtan uca akış:
Neden her iki taraf da eşi bağımsız olarak saklar
Başlatıcının (adım 9) ve yanıtlayıcının (adım 7) diğer cihaz için
PairingPeerStore.save(...) çağırdığına dikkat edin. Bu kasıtlıdır: Her
cihaz, diğerinin clientId'siyle anahtarlanan, kendi paylaşılan ChaCha20
anahtarı kopyasını ve diğerinin Ed25519 ortak anahtarını içeren bir DPeer
satırıyla sonuçlanır. Merkezi bir kayıt yoktur — eşleştirme simetrik ve
kendi kendine yetendir.
Yanıtlayıcı neden anahtarı önce hesaplar
Yanıtlayıcının acceptPairingRequest'i, kabul anında paylaşılan anahtarı
hemen hesaplar ve kalıcı hale getirir. Bu, yanıtlayıcının yanıt başlatıcıya
ulaşmadan önce şifreli trafiği almaya başlayabileceği anlamına gelir. Yanıt
yolda kaybolursa, yanıtlayıcı hala eşleştirilmiştir — yalnızca başlatıcının
yeniden denemesi gerekir.
Anahtar Değişimi Detayları {#key-exchange-details}
Eşleştirmenin kriptografik çekirdeği, standart bir X25519 tarzı ECDH anahtar anlaşmasıdır, ancak bunu doğrulamak için üzerine bir Ed25519 imzası eklenmiştir.
İmza gerçekte neyi korur
İmzalı yük (toSignatureData()), sabit istek alanlarının kurallı bir
birleştirmesidir: fromId, fromName, port, deviceType,
ecdhPublicKey, signaturePublicKey, timestamp ve ips. ecdhPublicKey'i
uzun vadeli signaturePublicKey ile birlikte imzalayarak, protokol kısa
ömürlü anahtarı cihazın kimliğine bağlar. Bir saldırgan, imzayı geçersiz
kılmadan kendi ECDH ortak anahtarını yolda değiştiremez — ve uzun vadeli
Ed25519 anahtarını kontrol etmeden imzayı da üretemez.
Bu, ortadaki adamı yenen şeydir: Saldırgan iki cihaz arasındaki her paketi aktarsa bile, şifreli trafiği okuyamaz (çünkü hiçbir tarafın ECDH özel anahtarına sahip değildir) ve kendi ECDH anahtarlarını değiştiremez (çünkü imzalar bozulur).
Yanıtlayıcı Kabul/Red Akışı {#responder-acceptdecline-flow}
Yanıtlayıcı tarafı, bir PAIR_REQUEST geldiğinde bir UI iletişim kutusu
gösterir. Kullanıcı kabul edebilir veya reddedebilir.
Yanıtlayıcı neden kabul sırasında PairingSuccessEvent'i hemen tetikler
Yanıtlayıcının acceptPairingRequest'i PairingPeerStore.save(...) çağırır
ve yanıtı göndermeden önce PairingSuccessEvent'i tetikler. Bu
kasıtlıdır: Yanıt hiçbir zaman başlatıcıya ulaşmazsa (ağ sorunu),
yanıtlayıcı hala eşleştirilmiştir — başlatıcı bir dahaki sefere eşleştirmeyi
denediğinde, yanıtlayıcının zaten var olan DPeer satırı varlık sistemi
tarafından alınır. Başlatıcı basitçe yeniden dener; yanıtlayıcının yeniden
onaylaması gerekmez.
İptal Akışı {#cancel-flow}
Her iki taraf da devam eden bir eşleştirmeyi iptal edebilir.
DPairingCancel'in yalnızca LAN tek noktaya yayın üzerinden gönderildiğine
dikkat edin (başlatıcı zaten keşif aşamasından yanıtlayıcının IP'sine
sahiptir), oysa red yanıtı hem LAN hem de BLE üzerinden gönderilir çünkü
yanıtlayıcı, başlatıcının hangi taşıma üzerinde ulaşılabilir olduğundan emin
olamaz.
Çift Kanal Teslimi (LAN + BLE) {#dual-channel-delivery-lan--ble}
Yanıtlayıcı DPairingResponse'u gönderdiğinde, bunu aynı anda hem LAN hem
de BLE üzerinden yapar. Başlatıcı ilk kopyayı kabul eder ve kopyayı
sessizce atar.
BlePairingSessionStore neden var
Bir PAIR_REQUEST BLE üzerinden geldiğinde, yanıtlayıcının başlatıcı için bir
LAN IP'si yoktur — yalnızca BLE MAC adresi vardır. BlePairingSessionStore,
peerId → MAC eşlemesini yapar, böylece gerektiğinde yanıt BLE üzerinden
geri yönlendirilebilir. Bu, yalnızca BLE yönlendirmeli istekler için doldurulan
ve yanıt gönderildikten sonra temizlenen küçük, bellekte, kısa ömürlü bir
eşlemdir.
Oturum ve Eş Depolama {#session--peer-storage}
Eşleştirmede iki depo yer alır, çok farklı yaşam süreleriyle:
clientId neden tek kalıcı tanımlayıcıdır
Android, BLE MAC adresini her bağlantıda rastgeleleştirir, bu nedenle saklamak
işe yaramaz. clientId, cihazın uzun vadeli Ed25519 anahtar materyalinden
türetilir, bu nedenle:
- Kararlı — uygulama yeniden yüklemelerinde (anahtar platform keystore'undadır).
- Kendi kendini doğrulayan — bir
clientIdiddia eden herkes, ilgili Ed25519 özel anahtarına sahip olduğunu kanıtlamalıdır (her imzalı mesajda doğrulanır). - Gizliliği koruyan — BLE üzerinden keşif için yalnızca 8 baytlık bir
SHA-256 öneki (
shortId) yayınlanır; tamclientIdyalnızca gerçekten eşleştirdiğiniz cihazlara açıklanır.
Güvenlik Özellikleri {#security-properties}
| Özellik | Nasıl sağlanır |
|---|---|
| Gizlilik | Tüm taşıma, ECDH'den türetilen paylaşılan anahtarla ChaCha20 ile şifrelenir. Anahtar, eşleştirmeden sonra iki cihazdan asla ayrılmaz. |
| Kimlik Doğrulama | Her imzalı mesaj (eşleştirme isteği/yanıtı, sohbet createChatItem, kanal invite/update/kick), gönderenin saklanan public_key'ine karşı Ed25519 ile doğrulanır. |
| Bütünlük | Ed25519 imzaları tam istek gövdesini kapsar; herhangi bir değişiklik imzayı geçersiz kılar. |
| Tekrar direnci | ±5 dakika zaman damgası penceresi (PeerChatParser ve PairingSecurity tarafından uygulanır). ChatMessageReceiver.seenSignatures pencere içinde yinelenenleri kaldırır. |
| Ortadaki adam direnci | Kısa ömürlü ECDH ortak anahtarı, uzun vadeli Ed25519 ortak anahtarıyla birlikte imzalanır. Bir MITM, imzayı bozmadan kendi ECDH anahtarını değiştiremez. |
| İleri gizlilik (sınırlı) | ECDH anahtar çiftleri, eşleştirme oturumu başına kısa ömürlüdür. Uzun vadeli Ed25519 anahtarının sonradan tehlikeye girmesi, geçmiş trafiğin şifresini çözemez (paylaşılan anahtar da hala gereklidir — ancak hem ECDH özel anahtarları hem de saklanan DPeer.key silinirse, geçmiş yakalamaların şifresi çözülemez). |
| Hizmet reddi direnci | onDatagram, her mesajı try/catch ile sarar, böylece bozuk bir paket keşif alıcısını öldüremez. PeerCircuitBreaker, 2 başarısızlıktan sonra 30 s süreyle güvenilmez bir taşımayı atlar. |
| Gizlilik (yönlendirilmiş keşif) | LANDiscoverManager.discoverSpecificDevice, hedef clientId'yi eşin anahtarıyla şifreler — pasif LAN gözlemcileri kimin kiminle eşleştirildiğini sayamaz. |
| Kimlik kararlılığı | clientId, platform keystore'undaki uzun vadeli Ed25519 anahtar materyalinden türetilir — yeniden yüklemelerde kararlı, kendi kendini doğrulayan ve bir telefon numarasına veya e-postaya bağlı olmayan. |
Eşleştirmenin savunmadığı şeyler
- Fiziksel cihaz tehlikesi. Bir saldırgan eşleştirilmiş bir cihazda root
elde ederse, paylaşılan anahtarı veritabanından okuyabilir ve o eşi taklit
edebilir. Paylaşılan taşıma anahtarı için donanım destekli anahtar deposu
zorunluluğu yoktur (yalnızca Ed25519 imzalama anahtarı için,
SignatureHelperaracılığıyla). - Aktif aktarma saldırıları. Kendileriyle eşleştiklerini düşünen iki cihaz arasında BLE ve LAN trafiğini aynı anda aktarabilen bir saldırgan, teorik olarak araya yerleşebilir — ancak ECDH ortak anahtarı üzerindeki Ed25519 imzası, trafiği okuyamayacakları, yalnızca aktarabilecekleri anlamına gelir. Bu, sayısal karşılaştırma olmadan Bluetooth eşleştirmeyle aynı takastır.
- Ağ düzeyinde engelleme. Bir güvenlik duvarı UDP çoklu yayını engelleyebilir, BLE sıkıştırılabilir ve Wi-Fi Aware kullanılamayabilir. Sistem zarif bir şekilde düşer (BLE, eşleştirilmiş eşler için garanti edilen yedektir) ancak aktif olarak düşmanca bir ağı atlayamaz.
Durum Makinesi Özeti {#state-machine-recap}
Daha Fazla Okuma
- Sohbet Mimarisi — paylaşılan anahtarın ne için kullanıldığı: eş sohbeti gönderme/alma, kanal dağıtımı, varlık, dosya indirmeleri.
apitest/groups/discovery.sh— keşif ve eşleştirme API yüzeyini uçtan uca çalıştıran yürütülebilir test planı.