Secure Enclave Yongası

Apple'ın ürettiği cihazlar epeydir Secure Enclave yongası ile geliyor. Bu yonga kendi OS'ine sahip mini bir bilgisayar neredeyse. Sistemden bağımsız bir yapısı var. Rastgele sayı üretebiliyor ve kripto işlemleri yapabiliyor. Ayrıca, Elliptic Curve özel anahtarları üretip içerisinde güvenli olarak saklıyor.

Bu anahtarları yonga dışına çıkarmak için herhangi bir komut barındırmadığından teorik olarak çok güvenli. Yonga içinde imzalama ve şifre çözme işlemlerini gerçekleştirebiliyorsunuz. Keychain'den daha güvenli bir anahtar barındırma hizmeti sağlıyor. Apple'ın konu ile ilgili şöyle bir makalesi mevcut.

Güvenli bir şekilde EC anahtar üretmek ile başlıyoruz.

        let authContext = LAContext();
        
        guard let accessControl = SecAccessControlCreateWithFlags(
            nil,
            kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
            [.privateKeyUsage,.userPresence],
            nil
            ) else {
                os_log(.error, "Unable to initialize access control flags")
                throw RuntimeError.invalidOperation(message: "Unable to initialize access control flags")
        }
        
        let privateKey = try! SecureEnclave.P256.Signing.PrivateKey(
            compactRepresentable: true,
            accessControl: accessControl,
            authenticationContext: authContext)

 

Anahtarı üretmeden önce, bu anahtarı kullanmak için nasıl bir erişim yöntemi olmasını gerektiğini belirliyoruz. Örnekte, sadece anahtarın üretildiği cihazda ve cihaz kilidi açıkken ayrıca kullanıcı kendi varlığını biyometrisi ile kanıtlamışken erişime izin verdik.

Burada tabii ki özel anahtarın kendisini değil referansını elde ediyoruz. Daha önce de söylediğim gibi özel anahtara ulaşmak mümkün değil.

Bu referansı bir yerde saklamamız lazım ki ihtiyacımız olduğunda kullanabilelim. Örneklerde CryptoKit kullandığımdan IOS 13 ve sonrasında çalışabilir.

        let query = [kSecClass: kSecClassGenericPassword,
                     kSecAttrAccount: "Daha sonra bulmak için buraya bir key yazılmalı",
                     kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
                     kSecUseDataProtectionKeychain: true,
                     kSecValueData: privateKey.dataRepresentation] as [String: Any]
        
        let status = SecItemAdd(query as CFDictionary, nil)
        guard status == errSecSuccess else {
            throw RuntimeError.invalidOperation(message: "Unable to store item: \(status)")
        }

Crpytokit ile keychain üzerinde GenericPassword gibi saklıyoruz. Çünkü sakladığımız referans String tipinde.

Bu referansı sonradan kSecAttrAccount'a yazdığımız değer ile geri çağırabiliriz.

        let query = [kSecClass: kSecClassGenericPassword,
                     kSecAttrAccount: "Daha önce verdiğimiz key değeri",
                     kSecUseDataProtectionKeychain: true,
                     kSecReturnData: true] as [String: Any]
        
        var item: CFTypeRef?
        let status = SecItemCopyMatching(query as CFDictionary, &item)
        switch status {
        case errSecSuccess:
            return item as! Data
            
        case errSecItemNotFound:
            throw RuntimeError.invalidOperation(message: "Key not found in keychain")
            
        default:
            throw RuntimeError.invalidOperation(message: "Unknown Error")
        }

veya kullanmıyorsak silebiliriz.

        let query = [kSecClass: kSecClassGenericPassword,
                     kSecAttrAccount: keyLabel,
                     kSecUseDataProtectionKeychain: true] as CFDictionary
        
        let status = SecItemDelete(query)
        guard status == errSecSuccess else {
            os_log(.error, "Unable to delete key: %d", status)
            return
        }

Data tipinde aldığımız özel anahtar referansı ile Secure Enclave yonga içinde imzalama yaptırabiliriz. Bu çağrı daha önce biyometri ayarladığımız için, parmak izi olan sistemlerde parmak izi doğrulama, Face id olanlarda yüz doğrulama, hiçbirine sahip olmayanlarda da pin ile doğrulama isteyecek.

        let signer = try SecureEnclave.P256.Signing.PrivateKey.init(dataRepresentation: key)
       
        do {
            let signedData = try signer.signature(for: data)
            return signedData.derRepresentation
        } catch {
            throw RuntimeError.invalidOperation(message: "Signing failed: \(error)")
        }