Skip to content

Commit 2c7aad2

Browse files
authored
WIP: Remove locksmith dependency (#37)
* refactor locksmith code to CelySecureStorage * replace locksmith with own implementation * add swift format comment * Replace LocksmithError with CelyKeychainStatus * Revert "Replace LocksmithError with CelyKeychainStatus" This reverts commit 608a66b. * Refactor keychain code to separate internal class CelyKeychain * update ownership on project * address PR comments
1 parent 8b4d341 commit 2c7aad2

22 files changed

+204
-1198
lines changed

Cely Demo/AppDelegate.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Cely Demo
44
//
55
// Created by Fabian Buentello on 3/27/17.
6-
// Copyright © 2017 ChaiOne. All rights reserved.
6+
// Copyright © 2017 Cely. All rights reserved.
77
//
88

99
import UIKit

Cely Demo/Assets.xcassets/AppIcon.appiconset/Contents.json

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
"idiom" : "ipad",
8585
"size" : "83.5x83.5",
8686
"scale" : "2x"
87+
},
88+
{
89+
"idiom" : "ios-marketing",
90+
"size" : "1024x1024",
91+
"scale" : "1x"
8792
}
8893
],
8994
"info" : {

Cely Demo/Cely Demo.entitlements

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<dict>
55
<key>keychain-access-groups</key>
66
<array>
7-
<string>$(AppIdentifierPrefix)com.chaione.Cely-Demo</string>
7+
<string>$(AppIdentifierPrefix)com.cely.Cely-Demo</string>
88
</array>
99
</dict>
1010
</plist>

Cely Demo/LoginStyles.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Cely
1212
struct LoginStyles: CelyStyle {
1313

1414
func appLogo() -> UIImage? {
15-
return UIImage(named: "ChaiOneLogo")
15+
return UIImage(named: "Cely")
1616
}
1717
}
1818

Cely.xcodeproj/project.pbxproj

+45-78
Large diffs are not rendered by default.

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2017 ChaiOne
3+
Copyright (c) 2017 Cely
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Sources/Cely.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Cely
44
//
55
// Created by Fabian Buentello on 04/10/16.
6-
// Copyright © 2017 ChaiOne. All rights reserved.
6+
// Copyright © 2017 Cely. All rights reserved.
77
//
88

99
#import <Foundation/Foundation.h>

Sources/CelyKeychain.swift

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// CelyKeychain.swift
3+
// Cely-iOS
4+
//
5+
// Created by Fabian Buentello on 8/7/19.
6+
// Copyright © 2019 Fabian Buentello. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
internal struct CelyKeychain {
12+
13+
// query to identify Cely Credentials
14+
private let baseQuery: [String: Any] = [
15+
kSecClass as String: kSecClassInternetPassword,
16+
kSecAttrLabel as String: "cely.secure.store.key",
17+
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
18+
]
19+
20+
private var searchQuery: [String: Any] {
21+
var queryCopy = baseQuery
22+
queryCopy[kSecMatchLimit as String] = kSecMatchLimitOne
23+
queryCopy[kSecReturnAttributes as String] = true
24+
return queryCopy
25+
}
26+
27+
func clearKeychain() -> StorageResult {
28+
let status = SecItemDelete(baseQuery as CFDictionary)
29+
let errorStatus = LocksmithError(fromStatusCode: Int(status))
30+
guard errorStatus == .noError
31+
else { return StorageResult.fail(errorStatus) }
32+
return .success
33+
}
34+
35+
func getCredentials() throws -> [String: Any] {
36+
var item: CFTypeRef?
37+
let status = SecItemCopyMatching(searchQuery as CFDictionary, &item)
38+
guard status != errSecItemNotFound else { throw LocksmithError.notFound }
39+
guard status == errSecSuccess else { throw LocksmithError.undefined }
40+
41+
do {
42+
guard let existingItem = item as? [String : Any],
43+
let secureData = existingItem[kSecValueData as String] as? Data,
44+
let loadedDictionary = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(secureData) as? [String : Any]
45+
else { throw LocksmithError.decode }
46+
return loadedDictionary
47+
}
48+
}
49+
50+
func set(_ secrets: [String : Any]) -> StorageResult {
51+
var queryCopy = baseQuery
52+
let storeData = NSKeyedArchiver.archivedData(withRootObject: secrets)
53+
queryCopy[kSecValueData as String] = storeData
54+
55+
// try adding first
56+
let status: OSStatus = SecItemAdd(queryCopy as CFDictionary, nil)
57+
let err = LocksmithError(fromStatusCode: Int(status))
58+
if err == .noError {
59+
return .success
60+
} else if err == .duplicate {
61+
// already exists, should update instead
62+
return update(secrets: secrets)
63+
}
64+
65+
return .fail(err)
66+
}
67+
68+
private func update(secrets: [String : Any]) -> StorageResult {
69+
let secretData = NSKeyedArchiver.archivedData(withRootObject: secrets)
70+
let updateDictionary = [kSecValueData as String: secretData]
71+
let status: OSStatus = SecItemUpdate(baseQuery as CFDictionary, updateDictionary as CFDictionary)
72+
let err = LocksmithError(fromStatusCode: Int(status))
73+
if err == .noError {
74+
return .success
75+
}
76+
return .fail(err)
77+
}
78+
}

Sources/CelySecureStorage.swift

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// CelySecureStorage.swift
3+
// Cely-iOS
4+
//
5+
// Created by Fabian Buentello on 7/27/19.
6+
// Copyright © 2019 Fabian Buentello. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
internal class CelySecureStorage {
12+
var store: [String : Any] = [:]
13+
private let _celyKeychain = CelyKeychain()
14+
15+
init() {
16+
do {
17+
let credentials = try _celyKeychain.getCredentials()
18+
store = credentials
19+
} catch let error as LocksmithError {
20+
print("Failed to retrieve store from keychain")
21+
print(error)
22+
} catch {
23+
print("Failed to retrieve store from keychain")
24+
print(error)
25+
}
26+
}
27+
28+
func clearStorage() {
29+
let status = _celyKeychain.clearKeychain()
30+
if status != .success {
31+
print("Failed to clear keychain, error: \(status)")
32+
} else {
33+
store = [:]
34+
}
35+
}
36+
37+
func set(_ value: Any, forKey key: String) -> StorageResult {
38+
var storeCopy = store
39+
storeCopy[key] = value
40+
let result = _celyKeychain.set(storeCopy)
41+
if result == .success {
42+
store[key] = value
43+
}
44+
return result
45+
}
46+
47+
func get(_ key: String) -> Any? {
48+
return store[key]
49+
}
50+
}

Sources/CelyStorage.swift

+11-34
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
import Foundation
1010

1111
internal let kCelyDomain = "cely.storage"
12-
internal let kCelyLocksmithAccount = "cely.secure.storage"
13-
internal let kCelyLocksmithService = "cely.secure.service"
1412
internal let kStore = "store"
1513
internal let kPersisted = "persisted"
1614
internal let kLaunchedBefore = "launchedBefore"
@@ -19,7 +17,12 @@ public class CelyStorage: CelyStorageProtocol {
1917
// MARK: - Variables
2018
static let sharedInstance = CelyStorage()
2119

22-
var secureStorage: [String : Any] = [:]
20+
fileprivate let secureStore = CelySecureStorage()
21+
22+
var secureStorage: [String : Any] {
23+
return secureStore.store
24+
}
25+
2326
var storage: [String : [String : Any]] = [:]
2427
public init() {
2528

@@ -31,14 +34,11 @@ public class CelyStorage: CelyStorageProtocol {
3134
UserDefaults.standard.setPersistentDomain([kStore: [:], kPersisted: [kLaunchedBefore:true]], forName: kCelyDomain)
3235
UserDefaults.standard.synchronize()
3336

34-
// Clear Locksmith
35-
do {
36-
try Locksmith.deleteDataForUserAccount(userAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService)
37-
} catch {}
37+
// Clear Secure Storage
38+
secureStore.clearStorage()
3839
}
3940

4041
setupStorage()
41-
setupSecureStorage()
4242
}
4343

4444
fileprivate func setupStorage() {
@@ -50,24 +50,12 @@ public class CelyStorage: CelyStorageProtocol {
5050
}
5151
}
5252

53-
fileprivate func setupSecureStorage() {
54-
if let userData = Locksmith.loadDataForUserAccount(userAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService) {
55-
secureStorage = userData
56-
}
57-
}
58-
5953
/// Removes all data from both `secureStorage` and regular `storage`
6054
public func removeAllData() {
61-
CelyStorage.sharedInstance.secureStorage = [:]
6255
CelyStorage.sharedInstance.storage[kStore] = [:]
6356
UserDefaults.standard.setPersistentDomain(CelyStorage.sharedInstance.storage, forName: kCelyDomain)
6457
UserDefaults.standard.synchronize()
65-
do {
66-
try Locksmith.deleteDataForUserAccount(userAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService)
67-
} catch {
68-
// handle the error
69-
print("error: \(error.localizedDescription)")
70-
}
58+
CelyStorage.sharedInstance.secureStore.clearStorage()
7159
}
7260

7361
/// Saves data to storage
@@ -81,18 +69,7 @@ public class CelyStorage: CelyStorageProtocol {
8169
public func set(_ value: Any?, forKey key: String, securely secure: Bool = false, persisted: Bool = false) -> StorageResult {
8270
guard let val = value else { return .fail(.undefined) }
8371
if secure {
84-
var currentStorage = CelyStorage.sharedInstance.secureStorage
85-
currentStorage[key] = val
86-
CelyStorage.sharedInstance.secureStorage = currentStorage
87-
do {
88-
try Locksmith.updateData(data: currentStorage, forUserAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService)
89-
return .success
90-
91-
} catch let storageError as LocksmithError {
92-
return .fail(storageError)
93-
} catch {
94-
return .fail(.undefined)
95-
}
72+
return secureStore.set(val, forKey: key)
9673
} else {
9774
if persisted {
9875
CelyStorage.sharedInstance.storage[kPersisted]?[key] = val
@@ -113,7 +90,7 @@ public class CelyStorage: CelyStorageProtocol {
11390
///
11491
/// - returns: Data For key value
11592
public func get(_ key: String) -> Any? {
116-
if let value = CelyStorage.sharedInstance.secureStorage[key] {
93+
if let value = CelyStorage.sharedInstance.secureStore.get(key) {
11794
return value
11895
} else if let value = CelyStorage.sharedInstance.storage[kStore]?[key] {
11996
return value

0 commit comments

Comments
 (0)