BAC (Basic Access Control) ve PACE (Password Authenticated Connection Establishment), elektronik pasaport veya kimlik kartı çipinin bir okuyucuya içeriğini gösterip göstermeyeceğine karar verirken kullandığı iki protokol. İkisi de ICAO Doc 9303 Part 11'de tanımlı. BAC, belgede basılı veriden sabit anahtarlar üretir; bu yüzden gücü o verinin entropisiyle sınırlı kalır. PACE de aynı türden kısa bir paroladan yola çıkar ama üzerine bir Diffie-Hellman anahtar anlaşması kurar, böylece oturum anahtarları parolanın zayıflığını devralmaz. Yeni belgelerde PACE kullanılıyor; okuyucular ise hâlâ dolaşımda olan çok sayıda eski çip için BAC desteğini koruyor.
Android'de NFC ile kimlik çipi okuma üzerinde çalışırken bu ayrımı netleştirmem gerekti. Okuma ve doğrulama akışının tamamını Android'de eID çipi okuma yazısında anlattım. Bu yazı o akışın tek adımına, erişim kontrolüne odaklanıyor.
Kimlik çipi neden erişim kontrolüne ihtiyaç duyar?
Temassız çiple ISO/IEC 14443 üzerinden konuşulur. Erişim kontrolü olmasa, yeterince yaklaşan biri adınızı, doğum tarihinizi ve fotoğrafınızı siz fark etmeden okuyabilir ya da belgeniz ile meşru bir terminal arasındaki trafiği kaydedebilir. Bu yüzden çip tek bir veri grubunu vermeden önce okuyucunun belgeyi okumaya yetkili olduğunu göstermesini ister.
ICAO tasarımında "yetkili" olmak, ancak belgeyi elinde tutan birinin bilebileceği bir şeyi bilmek demek: makine tarafından okunabilir alan (MRZ), yani pasaport veri sayfasının altındaki iki satır ya da kimlik kartının arkasındaki üç satır. Yeni belgelerde bunun yerine kartın üzerinde basılı, genellikle altı haneli CAN (Card Access Number) da kullanılabilir. Varsayım şu: MRZ'yi ya da CAN'i okuyabiliyorsanız belge elinizdedir.
Bu mekanizma mahremiyetle ilgili, verinin güvenilirliğiyle değil. İçeriğin gerçek olup olmadığı ayrı bir soru: Passive Authentication düzenleyen makamın veri üzerindeki imzasını, Active Authentication veya Chip Authentication ise çipin klon olmadığını kontrol eder. Bu ikisini ayrı tutmak akışın geri kalanını anlamayı çok kolaylaştırıyor.
BAC: MRZ'den türetilen anahtarlar
BAC, ilk nesil e-pasaportlarla geldi. Okuyucu üç MRZ alanını, her birinin arkasına kontrol hanesini ekleyerek birleştirir: belge numarası, doğum tarihi ve son geçerlilik tarihi. Bu dizinin SHA-1 hash'ini alır ve ilk 16 byte'ı anahtar tohumu K_seed olarak kullanır. Tohumdan iki anahtar türetilir: şifreleme için K_enc, MAC için K_mac. İkisi de iki anahtarlı 3DES anahtarıdır:
import java.security.MessageDigest
private val CHECK_WEIGHTS = intArrayOf(7, 3, 1)
/** ICAO 9303 check digit: weights 7-3-1, '<' counts as 0, letters A=10..Z=35. */
fun checkDigit(field: String): Char {
val sum = field.foldIndexed(0) { index, acc, c ->
val value = when (c) {
in '0'..'9' -> c - '0'
in 'A'..'Z' -> c - 'A' + 10
'<' -> 0
else -> throw IllegalArgumentException("Invalid MRZ character: $c")
}
acc + value * CHECK_WEIGHTS[index % 3]
}
return '0' + sum % 10
}
/** Dates are YYMMDD. Document numbers shorter than 9 characters are padded with '<'. */
fun bacKeySeed(documentNumber: String, dateOfBirth: String, dateOfExpiry: String): ByteArray {
val docNumber = documentNumber.padEnd(9, '<')
val mrzInfo = docNumber + checkDigit(docNumber) +
dateOfBirth + checkDigit(dateOfBirth) +
dateOfExpiry + checkDigit(dateOfExpiry)
val sha1 = MessageDigest.getInstance("SHA-1").digest(mrzInfo.toByteArray(Charsets.US_ASCII))
return sha1.copyOf(16)
}
/** counter = 1 gives K_enc, counter = 2 gives K_mac (16 bytes, DES parity not yet adjusted). */
fun deriveKey(keySeed: ByteArray, counter: Int): ByteArray {
val input = keySeed + byteArrayOf(0, 0, 0, counter.toByte())
return MessageDigest.getInstance("SHA-1").digest(input).copyOf(16)
}
Okuyucu ve çip bu anahtarlarla birbirini doğrular. Okuyucu GET CHALLENGE gönderir ve çipten 8 byte'lık bir rastgele değer alır. Sonra kendi rastgele değerini ve kendi anahtar malzemesini üretir, hepsini K_enc ile şifreler, K_mac ile MAC'ler ve EXTERNAL AUTHENTICATE ile gönderir. Çip de kendi anahtar malzemesiyle aynı şekilde cevap verir. İki taraf bu iki anahtar malzemesini XOR'layıp yeni bir tohum elde eder ve oturum anahtarlarını ondan türetir. Bundan sonra her APDU, sıra sayacı (send sequence counter) kullanan 3DES Secure Messaging üzerinden gider.
Zayıflık doğrudan protokolün tasarımından geliyor. K_enc ve K_mac aynı belge için hiç değişmez ve tamamen basılı, entropisi düşük veriden gelir. Doğum tarihinin makul değerleri on binler mertebesinde, on yıllık geçerlilik aralığındaki son geçerlilik tarihininki birkaç bin civarında; birçok ülkede belge numaraları da tahmin edilebilir bir düzen izliyor. Sonuçta 112 bitlik bir 3DES anahtarı çıkıyor ama gerçek arama uzayını bu alanlar belirliyor. ICAO da BAC anahtarlarının entropisinin düşük olduğunu kendisi belirtiyor.
Pratikte şu demek: tek bir BAC oturumunu kaydeden saldırgan, kaydettiği EXTERNAL AUTHENTICATE mesajını alıp olası MRZ değerleri için anahtarları çevrimdışı hesaplayabilir ve her birini MAC'e karşı kontrol edebilir. Eşleşen aday bulununca mesajlardaki anahtar malzemesi çözülür, oturum anahtarları yeniden hesaplanır ve bütün oturum okunur. Kayıttan sonra bunların hiçbiri için çipe gerek yok.
PACE: parola doğrulamalı anahtar değişimi
PACE'i BSI tasarladı; ilk olarak 2010'da Alman kimlik kartında kullanıldı, sonra ICAO tarafından Supplemental Access Control (SAC) kapsamında benimsendi. PACE bir PAKE'dir (password-authenticated key exchange): zayıf olabilecek ortak bir paroladan, dinleyen birinin çevrimdışı elde edemeyeceği güçlü oturum anahtarları üretir.
Parola yine belge sahibinin elindeki bilgi. Ya MRZ'den türetilir (BAC'taki gibi üç alanın SHA-1'i) ya da CAN'in kendisidir. Parolanın entropisi yine düşük; değişen şey protokolün onunla ne yaptığı.
Her şey EF.CardAccess dosyasıyla (dosya kimliği 011C) başlar. Okuyucu bu dosyayı kimlik doğrulaması olmadan okuyabilir. İçinde SecurityInfos yapısı bulunur; buradaki PACEInfo girdisi protokolü bir OID ile, domain parametrelerini de bir kimlik numarasıyla belirtir. Örneğin id-PACE-ECDH-GM-AES-CBC-CMAC-128 (0.4.0.127.0.7.2.2.4.2.2) ECDH, Generic Mapping ve AES-128 Secure Messaging anlamına gelir; parametre kimliği 13 ise brainpoolP256r1 eğrisidir.
Okuyucu MSE:Set AT ile protokolü ve parola türünü (MRZ veya CAN) seçer, ardından el sıkışma bir dizi GENERAL AUTHENTICATE komutuyla ilerler:
- Şifreli nonce. Paroladan bir
K_πanahtarı türetilir. Çip rastgele birsnonce'ı seçer,K_πile şifreleyip gönderir. Okuyucu aynı anahtarla çözer. - Mapping. İki taraf
s'i kullanarak yalnızca bu oturuma ait yeni bir üreteç hesaplar. Generic Mapping'de anonim bir Diffie-Hellman değişimi yapılır ve üreteçG' = s·G + Holur;H, bu değişimin ortak sırrıdır. Integrated Mapping'de okuyucu ikinci bir nonce gönderir; iki nonce sözde rastgele bir fonksiyondan geçirilir ve sonuç, ek bir DH turu olmadan doğrudan bir noktaya eşlenir. Doc 9303, Generic Mapping üzerine kurulu ve çipi aynı el sıkışmada doğrulayan Chip Authentication Mapping'i de tanımlar. - Anahtar anlaşması. İki taraf
G'üzerinde yeni geçici anahtar çiftleri üretip DH veya ECDH yapar.KS_encveKS_macortak sırdan türetilir. - Token değişimi. Her taraf, karşı tarafın geçici açık anahtarı üzerinden
KS_macile hesaplanmış bir MAC gönderir. Token'lar doğrulanırsa iki taraf da aynı anahtarlara ulaştığını, dolayısıyla karşı tarafın parolayı bildiğini anlar.
Bundan sonra Secure Messaging, OID'de belirtilen algoritmalarla çalışır: AES varyantlarında AES ve CMAC, 3DES varyantlarında 3DES ve Retail MAC.
Önemli olan şu: oturumun tam kaydına sahip pasif bir gözlemci parola tahminlerini çevrimdışı test edemez; BAC'ı kıran saldırı da tam olarak bu. Şifreli nonce hangi anahtarla çözülürse çözülsün rastgele görünen bir değer verir; yanlış tahmini doğrusundan ayırmak mümkün değildir ve bir tahmini kontrol etmek için geçici DH sırları gerekir. PACE, zayıf bir parolanın çevrimiçi tahmin edilmesini imkânsız kılmaz: çiple tekrar tekrar konuşabilen bir saldırgan her PACE denemesinde bir parola deneyebilir; hızı çipin cevap süresiyle ve çipin uyguladığı sınırlarla kısıtlıdır. PACE'in ortadan kaldırdığı şey, bir kayıttan çevrimdışı anahtar kurtarmadır.
Müzakere ve geri dönüş
Okuyucu serbestçe seçim yapmaz; çipin sunduğuna göre hareket eder. Android'de JMRTD ile akış kabaca şöyle:
import android.nfc.tech.IsoDep
import net.sf.scuba.smartcards.CardService
import org.jmrtd.BACKey
import org.jmrtd.PassportService
import org.jmrtd.lds.CardAccessFile
import org.jmrtd.lds.PACEInfo
fun openSecureChannel(isoDep: IsoDep, bacKey: BACKey): PassportService {
val service = PassportService(
CardService.getInstance(isoDep),
PassportService.NORMAL_MAX_TRANCEIVE_LENGTH,
PassportService.DEFAULT_MAX_BLOCKSIZE,
false,
false,
)
service.open()
var paceSucceeded = false
try {
val cardAccess = CardAccessFile(service.getInputStream(PassportService.EF_CARD_ACCESS))
val paceInfo = cardAccess.securityInfos.filterIsInstance<PACEInfo>().firstOrNull()
if (paceInfo != null) {
service.doPACE(
bacKey,
paceInfo.objectIdentifier,
PACEInfo.toParameterSpec(paceInfo.parameterId),
null,
)
paceSucceeded = true
}
} catch (e: Exception) {
// No EF.CardAccess, or PACE failed: fall back to BAC below.
}
// EF.CardAccess sits in the master file; the eMRTD application is selected afterwards.
service.sendSelectApplet(paceSucceeded)
if (!paceSucceeded) {
service.doBAC(bacKey)
}
return service
}
BACKey belge numarası, doğum tarihi ve son geçerlilik tarihini alır; CAN ile PACE için doPACE'e bunun yerine PACEKeySpec.createCANKey(can) verilir. BAC'ın CAN ile çalışan bir türü yok, bu durumda geri dönülecek bir protokol de kalmaz. Metot imzaları JMRTD sürümleri arasında biraz değişebiliyor, kullandığınız sürümle karşılaştırın. Gerçek kodda bütün hataları tek bir catch'e de toplamayın: "EF.CardAccess bulunamadı" ile "PACE çalıştı ama parola yanlış" farklı ele alınmalı. MRZ yanlış okunduysa aynı veriyle BAC'a dönmek de başarısız olur.
Takvime gelince: geçiş döneminde ICAO, PACE sunan çiplerin eski okuyucu sistemleri okuyabilsin diye BAC'ı da sunmaya devam etmesini bekledi. Dolaşımdaki birçok pasaportun ikisini birden desteklemesinin nedeni bu. Doc 9303'ün güncel sürümleri PACE'i uygulanması gereken mekanizma olarak sunuyor, BAC'ı uyumluluk için tutuyor. Ancak belgeler on yıla kadar geçerli kaldığı için yalnızca BAC destekleyen çipler belli bir tarihte ortadan kalkmayacak. Öte yandan Alman kimlik kartı gibi yalnızca PACE kullanan belgeler de var. Bugün yazılan bir okuyucu önce PACE'i denemeli, BAC'ı geri dönüş olarak tutmalı.
Android NFC okuyucusu için anlamı
Android tarafı iki protokol için de aynı: adaptörü reader mode'a alırsınız, IsoDep bağlantısı açarsınız ve APDU'lar gönderirsiniz. Reader mode, kart telefonun üzerindeyken sistemin ve diğer NFC uygulamalarının araya girmesini engeller:
import android.app.Activity
import android.nfc.NfcAdapter
import android.nfc.tech.IsoDep
class PassportReader(private val activity: Activity) {
private val adapter: NfcAdapter? = NfcAdapter.getDefaultAdapter(activity)
fun startReading(onConnected: (IsoDep) -> Unit) {
adapter?.enableReaderMode(
activity,
{ tag ->
IsoDep.get(tag)?.use { isoDep ->
isoDep.timeout = 10_000
isoDep.connect()
onConnected(isoDep)
}
},
NfcAdapter.FLAG_READER_NFC_A or
NfcAdapter.FLAG_READER_NFC_B or
NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
null,
)
}
fun stopReading() {
adapter?.disableReaderMode(activity)
}
}
FLAG_READER_NFC_A ile birlikte FLAG_READER_NFC_B'yi de açın: e-pasaportlar ve kimlik kartları hem ISO 14443 Type A hem Type B olarak geliyor; tek bayrakla bazı belgeler hiç algılanmaz. Reader mode callback'i ana thread'de çalışmaz, bu yüzden bloklayan IsoDep çağrıları orada sorun olmaz; ama UI güncellemelerini ana thread'e geri göndermeniz gerekir.
onConnected içindeki sıra sabit: EF.CardAccess'i oku, protokolü seç, Secure Messaging'i kur, sonra veri gruplarını seçip oku. El sıkışma yapılmadan gönderilen okuma isteklerine çip "security status not satisfied" (6982) hatasıyla cevap verir.
Hata yönetiminde erişim kontrolünü tek bir geçti/kaldı sonucuna indirmeyin. "Çipe ulaşılamadı", "el sıkışma başarısız" ve "veri okundu ama doğrulanamadı" farklı nedenlerden doğar ve farklı çözümler ister; log'larınız ve hata mesajlarınız bunları ayırt edebilmeli. Android'de cihaz tarafındaki diğer güvenlik kontrolleri, örneğin root ve tamper tespiti için SecureCheck yazısına bakabilirsiniz.
Toparlarken
BAC anahtarları sabittir, basılı veriden gelir ve kaydedilmiş bir oturumdan çevrimdışı kaba kuvvetle bulunabilir. PACE aynı parolayı kullanır ama oturum anahtarlarını parolanın entropisinden bağımsız hâle getirir. Okuyucu yazarken: EF.CardAccess'i kontrol edin, önce PACE'i deneyin, BAC'ı geri dönüş olarak tutun ve erişim kontrolünü (kim okuyabilir) okunan verinin güvenilirliğini doğrulamaktan (okunana güvenilir mi) ayrı tutun.
Sık sorulan sorular
- eID çiplerinde BAC ile PACE arasındaki fark nedir?
- BAC, üç MRZ alanından sabit 3DES anahtarları türetir; gücü bu verinin düşük entropisiyle sınırlıdır. PACE de aynı türden kısa bir parola kullanır ama üzerine geçici bir Diffie-Hellman anahtar anlaşması kurar, bu yüzden oturum anahtarları parolanın entropisine bağlı kalmaz.
- BAC neden kriptografik olarak zayıf kabul edilir?
- Erişim anahtarları sabittir ve tamamen belge numarası, doğum tarihi ve son geçerlilik tarihinden oluşur. Tek bir BAC oturumunu kaydeden saldırgan olası MRZ değerlerini çevrimdışı deneyip kaydedilen kimlik doğrulama mesajıyla karşılaştırabilir, ardından bütün oturumu çözebilir.
- NFC okuyucu PACE mi BAC mı kullanacağına nasıl karar verir?
- Kimlik doğrulaması gerektirmeden okunabilen EF.CardAccess dosyasını okur. Dosyada PACEInfo girdisi varsa orada yazan protokol ve domain parametreleriyle PACE çalıştırır; dosya yoksa ya da PACE sunulmuyorsa BAC'a döner.
- Erişim kontrolü çipteki verinin gerçek olduğunu kanıtlar mı?
- Hayır. Erişim kontrolü yalnızca çipi kimin okuyabileceğini belirler. Verinin düzenleyen makamca imzalandığını Passive Authentication, çipin kopya olmadığını Active Authentication veya Chip Authentication kanıtlar.