Skip to content

Commit

Permalink
feat: provide abstraction for keychain component
Browse files Browse the repository at this point in the history
  • Loading branch information
goncalo-frade-iohk committed Sep 27, 2023
1 parent 2dbeb14 commit 9390783
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 24 deletions.
12 changes: 12 additions & 0 deletions AtalaPrismSDK/Pluto/Sources/Domain/KeychainProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Domain
import Foundation

public protocol KeychainProvider {
func getKey(
service: String,
account: String,
tag: String?,
algorithm: KeychainStorableKeyProperties.KeyAlgorithm,
type: KeychainStorableKeyProperties.KeyType
) throws -> Data
}
6 changes: 6 additions & 0 deletions AtalaPrismSDK/Pluto/Sources/Domain/KeychainStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Domain
import Foundation

public protocol KeychainStore {
func addKey(_ key: KeychainStorableKey, service: String, account: String) throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ extension CDDIDPrivateKeyDAO: DIDPrivateKeyProvider {
}

extension CDKey {
func parseToStorableKey(keychain: KeychainDAO) throws -> StorableKey {
func parseToStorableKey(keychain: KeychainProvider) throws -> StorableKey {
switch self {
case let keychainKey as CDKeychainKey:
guard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private func storeKeychainKey(
keychainKey: KeychainStorableKey,
service: String,
account: String,
keychain: KeychainDAO
keychain: KeychainStore
) throws {
try keychain.addKey(
keychainKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Domain

struct CDDIDPrivateKeyDAO: CoreDataDAO {
typealias CoreDataObject = CDDIDPrivateKey
let keychain: KeychainDAO
let keychain: KeychainStore & KeychainProvider
let keychainService: String
let readContext: NSManagedObjectContext
let writeContext: NSManagedObjectContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,9 @@ import Security

struct KeychainDAO {
let accessGroup: String?

func addKey(_ key: KeychainStorableKey, service: String, account: String) throws {
let status = SecItemAdd(
try key.getSecKeyAddItemDictionary(service: service, account: account),
nil
)
guard status == errSecSuccess else {
throw PlutoError.errorSavingKeyOnKeychainWithStatus(status)
}
}
}

extension KeychainDAO: KeychainProvider {
func getKey(
service: String,
account: String,
Expand All @@ -24,13 +16,14 @@ struct KeychainDAO {
) throws -> Data {
switch algorithm {
case .genericPassword:
let attibutes: [CFString: Any] = [
var attributes: [CFString: Any] = [
kSecAttrService: service,
kSecAttrAccount: account,
kSecReturnData: true
]
accessGroup.map { attributes[kSecAttrAccessGroup] = $0 }
var item: CFTypeRef?
let status = SecItemCopyMatching(attibutes as CFDictionary, &item)
let status = SecItemCopyMatching(attributes as CFDictionary, &item)
switch status {
case errSecSuccess:
guard let data = item as? Data else {
Expand All @@ -43,13 +36,14 @@ struct KeychainDAO {
throw PlutoError.errorRetrivingKeyFromKeychainWithStatus(status)
}
case .rawKey:
let attibutes: [CFString: Any] = [
var attributes: [CFString: Any] = [
kSecClass: kSecClassKey,
kSecAttrApplicationLabel: (service + account).data(using: .utf8)!,
kSecReturnData: true
]
accessGroup.map { attributes[kSecAttrAccessGroup] = $0 }
var item: CFTypeRef?
let status = SecItemCopyMatching(attibutes as CFDictionary, &item)
let status = SecItemCopyMatching(attributes as CFDictionary, &item)
switch status {
case errSecSuccess:
guard let data = item as? Data else {
Expand All @@ -62,13 +56,14 @@ struct KeychainDAO {
throw PlutoError.errorRetrivingKeyFromKeychainWithStatus(status)
}
default:
let attibutes: [CFString: Any] = [
var attributes: [CFString: Any] = [
kSecClass: kSecClassKey,
kSecAttrApplicationLabel: service + account,
kSecReturnRef: true
]
accessGroup.map { attributes[kSecAttrAccessGroup] = $0 }
var item: CFTypeRef?
let status = SecItemCopyMatching(attibutes as CFDictionary, &item)
let status = SecItemCopyMatching(attributes as CFDictionary, &item)
switch status {
case errSecSuccess:
guard let item else {
Expand All @@ -89,8 +84,20 @@ struct KeychainDAO {
}
}

extension KeychainDAO: KeychainStore {
func addKey(_ key: KeychainStorableKey, service: String, account: String) throws {
let status = SecItemAdd(
try key.getSecKeyAddItemDictionary(service: service, account: account, accessGroup: accessGroup),
nil
)
guard status == errSecSuccess else {
throw PlutoError.errorSavingKeyOnKeychainWithStatus(status)
}
}
}

extension KeychainStorableKey {
func getSecKeyAddItemDictionary(service: String, account: String) throws -> CFDictionary {
func getSecKeyAddItemDictionary(service: String, account: String, accessGroup: String?) throws -> CFDictionary {
switch type {
case .genericPassword:
var attributes: [CFString : Any] = [
Expand All @@ -103,7 +110,8 @@ extension KeychainStorableKey {
kSecValueData: self.storableData as CFData,
kSecReturnData: true
]


accessGroup.map { attributes[kSecAttrAccessGroup] = $0 }
self.accessiblity.map { attributes[kSecAttrAccessible] = $0.secAccessible }
return attributes as CFDictionary
case .rawKey:
Expand All @@ -117,6 +125,7 @@ extension KeychainStorableKey {
kSecReturnData: true
]

accessGroup.map { attributes[kSecAttrAccessGroup] = $0 }
self.accessiblity.map { attributes[kSecAttrAccessible] = $0.secAccessible }
return attributes as CFDictionary
default:
Expand All @@ -128,6 +137,7 @@ extension KeychainStorableKey {
kSecValueRef: try getSecKeyDictionary()
]

accessGroup.map { attributes[kSecAttrAccessGroup] = $0 }
self.accessiblity.map { attributes[kSecAttrAccessible] = $0.secAccessible }
return attributes as CFDictionary
}
Expand Down
22 changes: 19 additions & 3 deletions AtalaPrismSDK/Pluto/Sources/PlutoImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,34 @@ public struct PlutoImpl {
public struct PlutoSetup {
public let coreDataSetup: CoreDataManager.CoreDataSetup
public let keychainService: String
public let keychainAccessGroup: String?
public let keychain: KeychainStore & KeychainProvider

public init(
coreDataSetup: CoreDataManager.CoreDataSetup = .init(
modelPath: .storeName("PrismPluto"),
storeType: .persistent
),
keychainService: String = "atala.prism.service",
keychainAccessGroup: String? = nil
) {
self.init(
coreDataSetup: coreDataSetup,
keychainService: keychainService,
keychain: KeychainDAO(accessGroup: keychainAccessGroup)
)
}

public init(
coreDataSetup: CoreDataManager.CoreDataSetup = .init(
modelPath: .storeName("PrismPluto"),
storeType: .persistent
),
keychainService: String = "atala.prism.service",
keychain: (KeychainStore & KeychainProvider)
) {
self.coreDataSetup = coreDataSetup
self.keychainService = keychainService
self.keychainAccessGroup = keychainAccessGroup
self.keychain = keychain
}
}

Expand All @@ -38,7 +54,7 @@ public struct PlutoImpl {
writeContext: manager.editContext
)
let privateKeyDao = CDDIDPrivateKeyDAO(
keychain: KeychainDAO(accessGroup: setup.keychainAccessGroup),
keychain: setup.keychain,
keychainService: setup.keychainService,
readContext: manager.mainContext,
writeContext: manager.editContext
Expand Down

0 comments on commit 9390783

Please sign in to comment.