damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 8b7b77c2e82e3924527bdf4d0d24a652200d1741
parent 1f6585e4193dd4f9ee2d47b2c85606c54cdbd7d6
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 25 Dec 2022 13:42:56 -0800

Merge PR #111: Save the private key to the iOS keychain and not user defaults

Closes: #111
Changelog-Changed: Save privkey in keychain instead of user defaults

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 17+++++++++++++++++
Mdamus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 9+++++++++
Mdamus/Util/Keys.swift | 43+++++++++++++++++++++++++++++++------------
Mdamus/Views/LoginView.swift | 28++++++++++++++++++++++++----
Mdamus/Views/SaveKeysView.swift | 9+++++++--
Mdamus/damus.entitlements | 4++++
Mdamus/damusApp.swift | 2+-
7 files changed, 93 insertions(+), 19 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -130,6 +130,7 @@ 4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; }; 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; }; E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; }; + 6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -316,6 +317,7 @@ buildActionMask = 2147483647; files = ( 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */, + 6C7DE41F2955169800E66263 /* Vault in Frameworks */, 4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */, 4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */, ); @@ -626,6 +628,7 @@ 4CE6DF1127F7A2B300C66700 /* Starscream */, 4C649880286E0EE300EAE2B3 /* secp256k1 */, 4C06670328FC7EC500038D2A /* Kingfisher */, + 6C7DE41E2955169800E66263 /* Vault */, ); productName = damus; productReference = 4CE6DEE327F7A08100C66700 /* damus.app */; @@ -705,6 +708,7 @@ 4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */, 4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */, 3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */, + 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */, ); productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */; projectDirPath = ""; @@ -1242,6 +1246,14 @@ minimumVersion = 4.0.0; }; }; + 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SparrowTek/Vault"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1260,6 +1272,11 @@ package = 4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */; productName = Starscream; }; + 6C7DE41E2955169800E66263 /* Vault */ = { + isa = XCSwiftPackageProductDependency; + package = 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */; + productName = Vault; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 4CE6DEDB27F7A08100C66700 /* Project object */; diff --git a/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -34,6 +34,15 @@ "revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21", "version" : "4.0.4" } + }, + { + "identity" : "vault", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SparrowTek/Vault", + "state" : { + "revision" : "f5707fac23f4a17b3e5ed32dd444f502773615ae", + "version" : "1.0.2" + } } ], "version" : 2 diff --git a/damus/Util/Keys.swift b/damus/Util/Keys.swift @@ -7,6 +7,7 @@ import Foundation import secp256k1 +import Vault let PUBKEY_HRP = "npub" let PRIVKEY_HRP = "nsec" @@ -29,6 +30,12 @@ enum Bech32Key { case sec(String) } +struct DamusKeychainConfiguration: KeychainConfiguration { + var serviceName = "damus" + var accessGroup: String? = nil + var accountName = "privkey" +} + func decode_bech32_key(_ key: String) -> Bech32Key? { guard let decoded = try? bech32_decode(key) else { return nil @@ -86,32 +93,38 @@ func save_pubkey(pubkey: String) { UserDefaults.standard.set(pubkey, forKey: "pubkey") } -func save_privkey(privkey: String) { - UserDefaults.standard.set(privkey, forKey: "privkey") +func save_privkey(privkey: String) throws { + try Vault.savePrivateKey(privkey, keychainConfiguration: DamusKeychainConfiguration()) } -func clear_saved_privkey() { - UserDefaults.standard.removeObject(forKey: "privkey") +func clear_saved_privkey() throws { + try Vault.deletePrivateKey(keychainConfiguration: DamusKeychainConfiguration()) } func clear_saved_pubkey() { UserDefaults.standard.removeObject(forKey: "pubkey") } -func save_keypair(pubkey: String, privkey: String) { +func save_keypair(pubkey: String, privkey: String) throws { save_pubkey(pubkey: pubkey) - save_privkey(privkey: privkey) + try save_privkey(privkey: privkey) } -func clear_keypair() { - clear_saved_privkey() +func clear_keypair() throws { + try clear_saved_privkey() clear_saved_pubkey() } func get_saved_keypair() -> Keypair? { - get_saved_pubkey().flatMap { pubkey in - let privkey = get_saved_privkey() - return Keypair(pubkey: pubkey, privkey: privkey) + do { + try removePrivateKeyFromUserDefaults() + + return get_saved_pubkey().flatMap { pubkey in + let privkey = get_saved_privkey() + return Keypair(pubkey: pubkey, privkey: privkey) + } + } catch { + return nil } } @@ -120,5 +133,11 @@ func get_saved_pubkey() -> String? { } func get_saved_privkey() -> String? { - return UserDefaults.standard.string(forKey: "privkey") + try? Vault.getPrivateKey(keychainConfiguration: DamusKeychainConfiguration()) +} + +fileprivate func removePrivateKeyFromUserDefaults() throws { + guard let privKey = UserDefaults.standard.string(forKey: "privkey") else { return } + try save_privkey(privkey: privKey) + UserDefaults.standard.removeObject(forKey: "privkey") } diff --git a/damus/Views/LoginView.swift b/damus/Views/LoginView.swift @@ -52,14 +52,24 @@ struct LoginView: View { func process_login(_ key: ParsedKey, is_pubkey: Bool) -> Bool { switch key { case .priv(let priv): - save_privkey(privkey: priv) + do { + try save_privkey(privkey: priv) + } catch { + return false + } + guard let pk = privkey_to_pubkey(privkey: priv) else { return false } save_pubkey(pubkey: pk) case .pub(let pub): - clear_saved_privkey() + do { + try clear_saved_privkey() + } catch { + return false + } + save_pubkey(pubkey: pub) case .nip05(let id): @@ -82,10 +92,20 @@ struct LoginView: View { case .hex(let hexstr): if is_pubkey { - clear_saved_privkey() + do { + try clear_saved_privkey() + } catch { + return false + } + save_pubkey(pubkey: hexstr) } else { - save_privkey(privkey: hexstr) + do { + try save_privkey(privkey: hexstr) + } catch { + return false + } + guard let pk = privkey_to_pubkey(privkey: hexstr) else { return false } diff --git a/damus/Views/SaveKeysView.swift b/damus/Views/SaveKeysView.swift @@ -107,8 +107,13 @@ struct SaveKeysView: View { self.pool.send(.event(contacts_ev)) } - save_keypair(pubkey: account.pubkey, privkey: account.privkey) - notify(.login, account.keypair) + do { + try save_keypair(pubkey: account.pubkey, privkey: account.privkey) + notify(.login, account.keypair) + } catch { + self.error = "Failed to save keys" + } + case .error(let err): self.loading = false self.error = "\(err.debugDescription)" diff --git a/damus/damus.entitlements b/damus/damus.entitlements @@ -4,5 +4,9 @@ <dict> <key>aps-environment</key> <string>development</string> + <key>keychain-access-groups</key> + <array> + <string>$(AppIdentifierPrefix)com.jb55.damus2</string> + </array> </dict> </plist> diff --git a/damus/damusApp.swift b/damus/damusApp.swift @@ -36,7 +36,7 @@ struct MainView: View { } } .onReceive(handle_notify(.logout)) { _ in - clear_keypair() + try? clear_keypair() keypair = nil } .onAppear {