Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Remove locksmith dependency #37

Merged
merged 9 commits into from
Aug 15, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cely Demo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Cely Demo
//
// Created by Fabian Buentello on 3/27/17.
// Copyright © 2017 ChaiOne. All rights reserved.
// Copyright © 2017 Cely. All rights reserved.
//

import UIKit
Expand Down
5 changes: 5 additions & 0 deletions Cely Demo/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
Expand Down
2 changes: 1 addition & 1 deletion Cely Demo/Cely Demo.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.chaione.Cely-Demo</string>
<string>$(AppIdentifierPrefix)com.cely.Cely-Demo</string>
</array>
</dict>
</plist>
2 changes: 1 addition & 1 deletion Cely Demo/LoginStyles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Cely
struct LoginStyles: CelyStyle {

func appLogo() -> UIImage? {
return UIImage(named: "ChaiOneLogo")
return UIImage(named: "Cely")
}
}

Expand Down
123 changes: 45 additions & 78 deletions Cely.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017 ChaiOne
Copyright (c) 2017 Cely

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion Sources/Cely.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Cely
//
// Created by Fabian Buentello on 04/10/16.
// Copyright © 2017 ChaiOne. All rights reserved.
// Copyright © 2017 Cely. All rights reserved.
//

#import <Foundation/Foundation.h>
Expand Down
78 changes: 78 additions & 0 deletions Sources/CelyKeychain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// CelyKeychain.swift
// Cely-iOS
//
// Created by Fabian Buentello on 8/7/19.
// Copyright © 2019 Fabian Buentello. All rights reserved.
//

import Foundation

internal struct CelyKeychain {

// query to identify Cely Credentials
private let baseQuery: [String: Any] = [
kSecClass as String: kSecClassInternetPassword,
kSecAttrLabel as String: "cely.secure.store.key",
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]

private var searchQuery: [String: Any] {
var queryCopy = baseQuery
queryCopy[kSecMatchLimit as String] = kSecMatchLimitOne
queryCopy[kSecReturnAttributes as String] = true
return queryCopy
}

func clearKeychain() -> StorageResult {
let status = SecItemDelete(baseQuery as CFDictionary)
let errorStatus = LocksmithError(fromStatusCode: Int(status))
guard errorStatus == .noError
else { return StorageResult.fail(errorStatus) }
return .success
}

func getCredentials() throws -> [String: Any] {
var item: CFTypeRef?
let status = SecItemCopyMatching(searchQuery as CFDictionary, &item)
guard status != errSecItemNotFound else { throw LocksmithError.notFound }
guard status == errSecSuccess else { throw LocksmithError.undefined }

do {
guard let existingItem = item as? [String : Any],
let secureData = existingItem[kSecValueData as String] as? Data,
let loadedDictionary = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(secureData) as? [String : Any]
else { throw LocksmithError.decode }
return loadedDictionary
}
}

func set(_ secrets: [String : Any]) -> StorageResult {
var queryCopy = baseQuery
let storeData = NSKeyedArchiver.archivedData(withRootObject: secrets)
queryCopy[kSecValueData as String] = storeData

// try adding first
let status: OSStatus = SecItemAdd(queryCopy as CFDictionary, nil)
let err = LocksmithError(fromStatusCode: Int(status))
if err == .noError {
return .success
} else if err == .duplicate {
// already exists, should update instead
return update(secrets: secrets)
}

return .fail(err)
}

private func update(secrets: [String : Any]) -> StorageResult {
let secretData = NSKeyedArchiver.archivedData(withRootObject: secrets)
let updateDictionary = [kSecValueData as String: secretData]
let status: OSStatus = SecItemUpdate(baseQuery as CFDictionary, updateDictionary as CFDictionary)
let err = LocksmithError(fromStatusCode: Int(status))
if err == .noError {
return .success
}
return .fail(err)
}
}
50 changes: 50 additions & 0 deletions Sources/CelySecureStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// CelySecureStorage.swift
// Cely-iOS
//
// Created by Fabian Buentello on 7/27/19.
// Copyright © 2019 Fabian Buentello. All rights reserved.
//

import Foundation

internal class CelySecureStorage {
var store: [String : Any] = [:]
private let _celyKeychain = CelyKeychain()

init() {
do {
let credentials = try _celyKeychain.getCredentials()
store = credentials
} catch let error as LocksmithError {
print("Failed to retrieve store from keychain")
print(error)
} catch {
print("Failed to retrieve store from keychain")
print(error)
}
}

func clearStorage() {
let status = _celyKeychain.clearKeychain()
if status != .success {
print("Failed to clear keychain, error: \(status)")
} else {
store = [:]
}
}

func set(_ value: Any, forKey key: String) -> StorageResult {
var storeCopy = store
storeCopy[key] = value
let result = _celyKeychain.set(storeCopy)
if result == .success {
store[key] = value
}
return result
}

func get(_ key: String) -> Any? {
return store[key]
}
}
45 changes: 11 additions & 34 deletions Sources/CelyStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import Foundation

internal let kCelyDomain = "cely.storage"
internal let kCelyLocksmithAccount = "cely.secure.storage"
internal let kCelyLocksmithService = "cely.secure.service"
internal let kStore = "store"
internal let kPersisted = "persisted"
internal let kLaunchedBefore = "launchedBefore"
Expand All @@ -19,7 +17,12 @@ public class CelyStorage: CelyStorageProtocol {
// MARK: - Variables
static let sharedInstance = CelyStorage()

var secureStorage: [String : Any] = [:]
fileprivate let secureStore = CelySecureStorage()

var secureStorage: [String : Any] {
return secureStore.store
}

var storage: [String : [String : Any]] = [:]
public init() {

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

// Clear Locksmith
do {
try Locksmith.deleteDataForUserAccount(userAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService)
} catch {}
// Clear Secure Storage
secureStore.clearStorage()
}

setupStorage()
setupSecureStorage()
}

fileprivate func setupStorage() {
Expand All @@ -50,24 +50,12 @@ public class CelyStorage: CelyStorageProtocol {
}
}

fileprivate func setupSecureStorage() {
if let userData = Locksmith.loadDataForUserAccount(userAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService) {
secureStorage = userData
}
}

/// Removes all data from both `secureStorage` and regular `storage`
public func removeAllData() {
CelyStorage.sharedInstance.secureStorage = [:]
CelyStorage.sharedInstance.storage[kStore] = [:]
UserDefaults.standard.setPersistentDomain(CelyStorage.sharedInstance.storage, forName: kCelyDomain)
UserDefaults.standard.synchronize()
do {
try Locksmith.deleteDataForUserAccount(userAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService)
} catch {
// handle the error
print("error: \(error.localizedDescription)")
}
CelyStorage.sharedInstance.secureStore.clearStorage()
}

/// Saves data to storage
Expand All @@ -81,18 +69,7 @@ public class CelyStorage: CelyStorageProtocol {
public func set(_ value: Any?, forKey key: String, securely secure: Bool = false, persisted: Bool = false) -> StorageResult {
guard let val = value else { return .fail(.undefined) }
if secure {
var currentStorage = CelyStorage.sharedInstance.secureStorage
currentStorage[key] = val
CelyStorage.sharedInstance.secureStorage = currentStorage
do {
try Locksmith.updateData(data: currentStorage, forUserAccount: kCelyLocksmithAccount, inService: kCelyLocksmithService)
return .success

} catch let storageError as LocksmithError {
return .fail(storageError)
} catch {
return .fail(.undefined)
}
return secureStore.set(val, forKey: key)
} else {
if persisted {
CelyStorage.sharedInstance.storage[kPersisted]?[key] = val
Expand All @@ -113,7 +90,7 @@ public class CelyStorage: CelyStorageProtocol {
///
/// - returns: Data For key value
public func get(_ key: String) -> Any? {
if let value = CelyStorage.sharedInstance.secureStorage[key] {
if let value = CelyStorage.sharedInstance.secureStore.get(key) {
return value
} else if let value = CelyStorage.sharedInstance.storage[kStore]?[key] {
return value
Expand Down