Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MIRACLTrust.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Pod::Spec.new do |s|
s.name = "MIRACLTrust"
s.summary = "MIRACL Trust SDK for iOS"
s.requires_arc = true
s.version = "1.3.0"
s.version = "1.4.0"
s.license = { :type => "Apache2", :file => "LICENSE" }
s.author = { "MIRACL" => "[email protected]" }
s.homepage = "https://github.com/miracl/trust-sdk-ios"
Expand Down
30 changes: 25 additions & 5 deletions MIRACLTrust/MIRACLTrust-Sources/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import Foundation
/// Identifier of the project in the MIRACL Trust platform.
var projectId: String

/// User objects are kept in this storage when they are registered. By default, they are written into an internal for the application SQLite database.
var userStorage: UserStorage?

/// Base URL of the MIRACL platform.
var platformURL: URL

Expand All @@ -29,6 +26,9 @@ import Foundation
// Additional information that will be sent via `X-MIRACL-CLIENT` HTTP header.
var applicationInfo: String?

/// Type of the storage that could be configured.
var storageType: StorageType

override private init() {
projectId = ""
platformURL = MIRACL_API_URL
Expand All @@ -38,8 +38,8 @@ import Foundation
urlSessionConfiguration.timeoutIntervalForRequest = 30
urlSessionConfiguration.timeoutIntervalForResource = 300
deviceName = ""

loggingLevel = .none
storageType = .default(DefaultUserStorageOptions())
}

/// Builds ``Configuration`` objects.
Expand Down Expand Up @@ -69,13 +69,33 @@ import Foundation
}
}

/// Sets ``DefaultStorageOptions``.
///
/// The default storage is and encrypted SQLite database stored in the `documents` directory.
///
/// - Parameter closure: A closure that receives an inout ``DefaultUserStorageOptions`` instance
/// so you can modify its properties. Any changes you make inside the closure
/// are applied to the user storage setup.
/// - Returns: ``Configuration/Builder`` object.
@discardableResult
public func configureDefaultUserStorage(
_ closure: (inout DefaultUserStorageOptions) -> Void
) -> Builder {
var options = DefaultUserStorageOptions()
closure(&options)

configurationToBuild.storageType = .default(options)

return self
}

/// Set custom ``UserStorage`` implementation.
/// - Parameter userStorage: custom ``UserStorage`` implementation.
/// - Returns: Configuration.Builder object.
@discardableResult public func userStorage(
userStorage: UserStorage
) -> Builder {
configurationToBuild.userStorage = userStorage
configurationToBuild.storageType = .custom(userStorage)
return self
}

Expand Down
15 changes: 11 additions & 4 deletions MIRACLTrust/MIRACLTrust-Sources/MIRACLTrust.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,18 @@ import Foundation
projectId = configuration.projectId
deviceName = configuration.deviceName
urlSessionConfiguration = configuration.urlSessionConfiguration
userStorage =
configuration.userStorage ??
SQLiteUserStorage(
projectId: configuration.projectId

switch configuration.storageType {
case let .default(options):
userStorage = SQLiteUserStorage(
projectId: projectId,
directoryURL: options.directoryURL,
databaseName: options.storageName,
keychainAccessGroup: options.keychainAccessGroup
)
case let .custom(storage):
userStorage = storage
}

miraclAPI = API(
baseURL: configuration.platformURL,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

/// Configurable options for the default storage provided by MIRACL Trust iOS SDK.
///
/// The default storage that MIRACL Trust iOS SDK provides is an encrypted SQLite database stored at the `documents` directory.
public class DefaultUserStorageOptions {
/// Name of the storage file.
public var storageName: String = "miracl"

/// File system path used for default storage file.
public var directoryURL: URL?

/// [Keychain access group](https://developer.apple.com/documentation/security/sharing-access-to-keychain-items-among-a-collection-of-apps) identifier used to share a storage key between applications from the same company.
public var keychainAccessGroup: String?
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import Foundation

class SQLiteEncryptionHandler {
private let keychainAccessGroup: String?
private let keychainItemAccount = "com.miracl.keys.userstable"

init(keychainAccessGroup: String?) {
self.keychainAccessGroup = keychainAccessGroup
}

func createEncryptionKey() -> String? {
var data = Data(count: 256)
_ = data.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, 256, $0.baseAddress!)
}

let key = data.base64EncodedString()
let addQuery: [String: Any] = [
var addQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: keychainItemAccount,
kSecValueData as String: data.base64EncodedData(),
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
]

// Add keychain access group if needed.
if let keychainAccessGroup {
addQuery[kSecAttrAccessGroup as String] = keychainAccessGroup
}

let result: OSStatus = SecItemAdd(addQuery as CFDictionary, nil)
guard result == errSecSuccess else {
return nil
Expand All @@ -26,10 +36,16 @@ class SQLiteEncryptionHandler {
}

func loadEncryptionKey() -> String? {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
var query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecAttrAccount as String: keychainItemAccount,
kSecReturnData as String: true]

// Add keychain access group if needed.
if let keychainAccessGroup {
query[kSecAttrAccessGroup as String] = keychainAccessGroup
}

var queryResult: AnyObject?
SecItemCopyMatching(query as CFDictionary, &queryResult)
if let data = queryResult as? Data {
Expand All @@ -39,6 +55,39 @@ class SQLiteEncryptionHandler {
return nil
}

func updateEncryptionKeyAccessGroupIfNeeded() -> Bool {
if let keychainAccessGroup {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecAttrAccount as String: keychainItemAccount,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
kSecAttrAccessGroup as String: keychainAccessGroup,
kSecReturnData as String: true]
var queryResult: AnyObject?
SecItemCopyMatching(query as CFDictionary, &queryResult)

if queryResult == nil {
let getItemQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: keychainItemAccount
]

let updatedAttributes = [
kSecAttrAccessGroup as String: keychainAccessGroup
]

let result: OSStatus = SecItemUpdate(getItemQuery as CFDictionary, updatedAttributes as CFDictionary)
if result == errSecSuccess {
return true
} else {
return false
}
}
}

return true
}

func updateEncryptionKeyAccessibilityIfNeeded() -> Bool {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecMatchLimit as String: kSecMatchLimitOne,
Expand All @@ -54,11 +103,16 @@ class SQLiteEncryptionHandler {
kSecAttrAccount as String: keychainItemAccount
]

let updatedAttribute = [
var updatedAttributes = [
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
]

let result: OSStatus = SecItemUpdate(getItemQuery as CFDictionary, updatedAttribute as CFDictionary)
if let keychainAccessGroup {
let keychainAccessGroup: CFString = keychainAccessGroup as NSString
updatedAttributes[kSecAttrAccessGroup as String] = keychainAccessGroup
}

let result: OSStatus = SecItemUpdate(getItemQuery as CFDictionary, updatedAttributes as CFDictionary)
if result == errSecSuccess {
return true
} else {
Expand Down
Loading
Loading