From 16861fd9f340a42bd0340bafad07fa00e993fd01 Mon Sep 17 00:00:00 2001 From: Rob <30500864+RobChangCA@users.noreply.github.com> Date: Fri, 14 Mar 2025 15:57:10 -0600 Subject: [PATCH 1/3] feat: update logo (#1454) --- .../ios/implementation/KeychainHelper.swift | 57 +++++++++++++++++++ .../implementation/NativeTEKStamperImpl.swift | 52 +++++++++++------ 2 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 account-kit/rn-signer/ios/implementation/KeychainHelper.swift diff --git a/account-kit/rn-signer/ios/implementation/KeychainHelper.swift b/account-kit/rn-signer/ios/implementation/KeychainHelper.swift new file mode 100644 index 0000000000..99caf5c2db --- /dev/null +++ b/account-kit/rn-signer/ios/implementation/KeychainHelper.swift @@ -0,0 +1,57 @@ +import Foundation +import Security +import CryptoKit + + +class KeychainHelper { + static let shared = KeychainHelper() // Singleton instance + + private init() {} // Prevent external instantiation + + private let keyIdentifier = "ephemeralPrivateKey" + + func savePrivateKeyToKeychain(_ privateKey: P256.KeyAgreement.PrivateKey) { + let keyData = privateKey.rawRepresentation + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: keyIdentifier, + kSecValueData as String: keyData, + kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock + ] + + SecItemDelete(query as CFDictionary) + SecItemAdd(query as CFDictionary, nil) + } + + func getPrivateKeyFromKeychain() -> P256.KeyAgreement.PrivateKey? { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: keyIdentifier, + kSecReturnData as String: true, + kSecMatchLimit as String: kSecMatchLimitOne + ] + + var result: AnyObject? + let status = SecItemCopyMatching(query as CFDictionary, &result) + + if status == errSecSuccess, let keyData = result as? Data { + do { + return try P256.KeyAgreement.PrivateKey(rawRepresentation: keyData) + } catch { + print("❌ Failed to decode private key: \(error)") + return nil + } + } + + return nil + } + + func deletePrivateKeyFromKeychain() { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: keyIdentifier + ] + SecItemDelete(query as CFDictionary) + } +} diff --git a/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift b/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift index 5cc89ef4cb..257207c9c3 100644 --- a/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift +++ b/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift @@ -1,3 +1,4 @@ + import CryptoKit import Foundation @@ -11,22 +12,28 @@ enum StamperError: Error { @objc public class NativeTEKStamperImpl: NSObject { // TODO: we probably want to keep this longer term somewhere, because the RN session manager will // hold on to the bundle and try to recreate a session if a user is still logged in - var ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = nil; + var ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = nil // These can be ephemeral and held in memory because the session manager will handle re-authenticating var apiPublicKey: P256.Signing.PublicKey? = nil; var apiPrivateKey: P256.Signing.PrivateKey? = nil; @objc public func create() async throws -> NSString { - if (ephemeralPrivateKey == nil) { - ephemeralPrivateKey = P256.KeyAgreement.PrivateKey() - } - let targetPublicKey = try ephemeralPrivateKey!.publicKey.toString(representation: .x963) + var _ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = KeychainHelper.shared.getPrivateKeyFromKeychain() + if (_ephemeralPrivateKey == nil) { + _ephemeralPrivateKey = P256.KeyAgreement.PrivateKey() + KeychainHelper.shared.savePrivateKeyToKeychain(_ephemeralPrivateKey!) + } + + let targetPublicKey = try _ephemeralPrivateKey!.publicKey.toString(representation: .x963) + ephemeralPrivateKey = _ephemeralPrivateKey + return NSString(string: targetPublicKey) } @objc public func clear() { + KeychainHelper.shared.deletePrivateKeyFromKeychain() ephemeralPrivateKey = nil apiPublicKey = nil apiPrivateKey = nil @@ -41,18 +48,30 @@ enum StamperError: Error { } } - @objc public func injectCredentialBundle(bundle: NSString) async throws -> ObjCBool { - if let ephemeralPrivateKey = ephemeralPrivateKey { - let (bundlePrivateKey, bundlePublicKey) = try AuthManager.decryptBundle(encryptedBundle: bundle as String, ephemeralPrivateKey: ephemeralPrivateKey) + @objc public func injectCredentialBundle(bundle: NSString) async throws -> ObjCBool { + if let ephemeralPrivateKey = ephemeralPrivateKey { + + do { + let (bundlePrivateKey, bundlePublicKey) = try AuthManager.decryptBundle( + encryptedBundle: bundle as String, + ephemeralPrivateKey: ephemeralPrivateKey + ) + + apiPublicKey = bundlePublicKey + apiPrivateKey = bundlePrivateKey - apiPublicKey = bundlePublicKey - apiPrivateKey = bundlePrivateKey - - return true; - } else { - throw StamperError.notInitialized - } - } + return true + } catch let error as AuthError { + print("Auth error: \(error)") + throw error + } catch { + print("Unknown error: \(error)") + throw error + } + } else { + throw StamperError.notInitialized + } + } // TODO: we should use the turnkey stamper for all of this, but we need it published as a pod // and it shouldn't require use_frameworks! @@ -70,7 +89,6 @@ enum StamperError: Error { let signatureHex = signature.derRepresentation.toHexString() - print(apiPublicKey.compressedRepresentation.toHexString()) let stamp: [String: Any] = [ "publicKey": apiPublicKey.compressedRepresentation.toHexString(), "scheme": "SIGNATURE_SCHEME_TK_API_P256", From 018b2c39eea258d3c7e3b3f8beca87fe2d3e299e Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Thu, 20 Mar 2025 14:27:20 -0400 Subject: [PATCH 2/3] fix: update keychain implementation to declear variables earlier during run --- .../ios/implementation/KeychainHelper.swift | 51 ++++++++++--------- .../implementation/NativeTEKStamperImpl.swift | 17 +------ 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/account-kit/rn-signer/ios/implementation/KeychainHelper.swift b/account-kit/rn-signer/ios/implementation/KeychainHelper.swift index 99caf5c2db..a6cc57363d 100644 --- a/account-kit/rn-signer/ios/implementation/KeychainHelper.swift +++ b/account-kit/rn-signer/ios/implementation/KeychainHelper.swift @@ -2,38 +2,43 @@ import Foundation import Security import CryptoKit +let account_kit_ephemeral_private_key = "ephemeralPrivateKey" + +// Keep save_ephemeral_private_key_query mutable +var save_ephemeral_private_key_query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: account_kit_ephemeral_private_key, + kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock +] + +let get_ephemeral_private_key_query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: account_kit_ephemeral_private_key, + kSecReturnData as String: true, + kSecMatchLimit as String: kSecMatchLimitOne +] + +let delete_ephemeral_private_key_query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: account_kit_ephemeral_private_key +] + class KeychainHelper { static let shared = KeychainHelper() // Singleton instance private init() {} // Prevent external instantiation - - private let keyIdentifier = "ephemeralPrivateKey" func savePrivateKeyToKeychain(_ privateKey: P256.KeyAgreement.PrivateKey) { let keyData = privateKey.rawRepresentation - - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: keyIdentifier, - kSecValueData as String: keyData, - kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock - ] - - SecItemDelete(query as CFDictionary) - SecItemAdd(query as CFDictionary, nil) + save_ephemeral_private_key_query.updateValue(keyData, forKey: kSecValueData as String) + SecItemDelete(save_ephemeral_private_key_query as CFDictionary) + SecItemAdd(save_ephemeral_private_key_query as CFDictionary, nil) } func getPrivateKeyFromKeychain() -> P256.KeyAgreement.PrivateKey? { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: keyIdentifier, - kSecReturnData as String: true, - kSecMatchLimit as String: kSecMatchLimitOne - ] - var result: AnyObject? - let status = SecItemCopyMatching(query as CFDictionary, &result) + let status = SecItemCopyMatching(get_ephemeral_private_key_query as CFDictionary, &result) if status == errSecSuccess, let keyData = result as? Data { do { @@ -48,10 +53,6 @@ class KeychainHelper { } func deletePrivateKeyFromKeychain() { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: keyIdentifier - ] - SecItemDelete(query as CFDictionary) + SecItemDelete(delete_ephemeral_private_key_query as CFDictionary) } } diff --git a/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift b/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift index 257207c9c3..6246953bbf 100644 --- a/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift +++ b/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift @@ -50,24 +50,11 @@ enum StamperError: Error { @objc public func injectCredentialBundle(bundle: NSString) async throws -> ObjCBool { if let ephemeralPrivateKey = ephemeralPrivateKey { - - do { - let (bundlePrivateKey, bundlePublicKey) = try AuthManager.decryptBundle( - encryptedBundle: bundle as String, - ephemeralPrivateKey: ephemeralPrivateKey - ) - + let (bundlePrivateKey, bundlePublicKey) = try AuthManager.decryptBundle(encryptedBundle: bundle as String, ephemeralPrivateKey: ephemeralPrivateKey) apiPublicKey = bundlePublicKey apiPrivateKey = bundlePrivateKey - return true - } catch let error as AuthError { - print("Auth error: \(error)") - throw error - } catch { - print("Unknown error: \(error)") - throw error - } + return true } else { throw StamperError.notInitialized } From 3c9d35e33edadfa9f55b75d2afbb36e9a18b5300 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Fri, 21 Mar 2025 10:29:48 -0400 Subject: [PATCH 3/3] feat: use struct for keychain utilities as opposed to class --- .../ios/implementation/KeychainHelper.swift | 34 +++++++++---------- .../implementation/NativeTEKStamperImpl.swift | 6 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/account-kit/rn-signer/ios/implementation/KeychainHelper.swift b/account-kit/rn-signer/ios/implementation/KeychainHelper.swift index a6cc57363d..7271eea9a9 100644 --- a/account-kit/rn-signer/ios/implementation/KeychainHelper.swift +++ b/account-kit/rn-signer/ios/implementation/KeychainHelper.swift @@ -2,41 +2,41 @@ import Foundation import Security import CryptoKit -let account_kit_ephemeral_private_key = "ephemeralPrivateKey" +let ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY = "accountKitEphemeralPrivateKey" -// Keep save_ephemeral_private_key_query mutable -var save_ephemeral_private_key_query: [String: Any] = [ +let save_ephemeral_private_key_query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: account_kit_ephemeral_private_key, + kSecAttrAccount as String: ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY, kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock ] - + let get_ephemeral_private_key_query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: account_kit_ephemeral_private_key, + kSecAttrAccount as String: ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] let delete_ephemeral_private_key_query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: account_kit_ephemeral_private_key + kSecAttrAccount as String: ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY ] -class KeychainHelper { - static let shared = KeychainHelper() // Singleton instance - - private init() {} // Prevent external instantiation +struct PrivateKeyChainUtilities { + @available(*, unavailable) private init() {} - func savePrivateKeyToKeychain(_ privateKey: P256.KeyAgreement.PrivateKey) { + static func savePrivateKeyToKeychain(_ privateKey: P256.KeyAgreement.PrivateKey) { let keyData = privateKey.rawRepresentation - save_ephemeral_private_key_query.updateValue(keyData, forKey: kSecValueData as String) - SecItemDelete(save_ephemeral_private_key_query as CFDictionary) - SecItemAdd(save_ephemeral_private_key_query as CFDictionary, nil) + + var _saveEphemeralPrivateKeyQuery = save_ephemeral_private_key_query + _saveEphemeralPrivateKeyQuery.updateValue(keyData, forKey: kSecValueData as String) + + SecItemDelete(_saveEphemeralPrivateKeyQuery as CFDictionary) + SecItemAdd(_saveEphemeralPrivateKeyQuery as CFDictionary, nil) } - func getPrivateKeyFromKeychain() -> P256.KeyAgreement.PrivateKey? { + static func getPrivateKeyFromKeychain() -> P256.KeyAgreement.PrivateKey? { var result: AnyObject? let status = SecItemCopyMatching(get_ephemeral_private_key_query as CFDictionary, &result) @@ -52,7 +52,7 @@ class KeychainHelper { return nil } - func deletePrivateKeyFromKeychain() { + static func deletePrivateKeyFromKeychain() { SecItemDelete(delete_ephemeral_private_key_query as CFDictionary) } } diff --git a/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift b/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift index 6246953bbf..d08a72f83a 100644 --- a/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift +++ b/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift @@ -19,11 +19,11 @@ enum StamperError: Error { var apiPrivateKey: P256.Signing.PrivateKey? = nil; @objc public func create() async throws -> NSString { - var _ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = KeychainHelper.shared.getPrivateKeyFromKeychain() + var _ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = PrivateKeyChainUtilities.getPrivateKeyFromKeychain() if (_ephemeralPrivateKey == nil) { _ephemeralPrivateKey = P256.KeyAgreement.PrivateKey() - KeychainHelper.shared.savePrivateKeyToKeychain(_ephemeralPrivateKey!) + PrivateKeyChainUtilities.savePrivateKeyToKeychain(_ephemeralPrivateKey!) } let targetPublicKey = try _ephemeralPrivateKey!.publicKey.toString(representation: .x963) @@ -33,7 +33,7 @@ enum StamperError: Error { } @objc public func clear() { - KeychainHelper.shared.deletePrivateKeyFromKeychain() + PrivateKeyChainUtilities.deletePrivateKeyFromKeychain() ephemeralPrivateKey = nil apiPublicKey = nil apiPrivateKey = nil