@onekeyfe/react-native-keychain-module 1.1.19 → 1.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/KeychainModule.podspec
CHANGED
package/ios/KeychainModule.swift
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import NitroModules
|
|
2
2
|
|
|
3
3
|
class KeychainModule: HybridKeychainModuleSpec {
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
private let moduleCore = KeychainModuleCore()
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
public func setItem(params: SetItemParams) throws -> Promise<Void> {
|
|
8
|
-
let typedParams = params
|
|
9
8
|
do {
|
|
10
|
-
try moduleCore.setItem(params:
|
|
9
|
+
try moduleCore.setItem(params: params)
|
|
11
10
|
return Promise.resolved(withResult: Void())
|
|
12
11
|
} catch let error as KeychainModuleError {
|
|
13
12
|
switch error {
|
|
@@ -22,11 +21,10 @@ class KeychainModule: HybridKeychainModuleSpec {
|
|
|
22
21
|
return Promise.rejected(withError: NSError(domain: "keychain_set_error", code: -1000, userInfo: [NSLocalizedDescriptionKey: "Failed to set keychain item", NSUnderlyingErrorKey: error]))
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
|
-
|
|
24
|
+
|
|
26
25
|
public func getItem(params : GetItemParams) throws -> Promise<Variant_NullType_GetItemResult> {
|
|
27
|
-
let typedParams = params
|
|
28
26
|
do {
|
|
29
|
-
if let result = try moduleCore.getItem(params:
|
|
27
|
+
if let result = try moduleCore.getItem(params: params) {
|
|
30
28
|
return Promise.resolved(withResult: Variant_NullType_GetItemResult.second(result))
|
|
31
29
|
} else {
|
|
32
30
|
return Promise.resolved(withResult: Variant_NullType_GetItemResult.first(NullType.null))
|
|
@@ -42,11 +40,10 @@ class KeychainModule: HybridKeychainModuleSpec {
|
|
|
42
40
|
return Promise.rejected(withError: NSError(domain: "keychain_get_error", code: -1000, userInfo: [NSLocalizedDescriptionKey: "Failed to get keychain item", NSUnderlyingErrorKey: error]))
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
|
-
|
|
43
|
+
|
|
46
44
|
public func removeItem(params: RemoveItemParams) throws -> Promise<Void> {
|
|
47
|
-
let typedParams = params
|
|
48
45
|
do {
|
|
49
|
-
try moduleCore.removeItem(params:
|
|
46
|
+
try moduleCore.removeItem(params: params)
|
|
50
47
|
return Promise.resolved(withResult: Void())
|
|
51
48
|
} catch let error as KeychainModuleError {
|
|
52
49
|
switch error {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import Foundation
|
|
10
10
|
import Security
|
|
11
|
+
import ReactNativeNativeLogger
|
|
11
12
|
|
|
12
13
|
// MARK: - Constants
|
|
13
14
|
|
|
@@ -33,10 +34,11 @@ class KeychainModuleCore {
|
|
|
33
34
|
|
|
34
35
|
func setItem(params: SetItemParams) throws {
|
|
35
36
|
guard let valueData = params.value.data(using: .utf8) else {
|
|
37
|
+
OneKeyLog.error("Keychain", "setItem: failed to encode value")
|
|
36
38
|
throw KeychainModuleError.encodingFailed
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
let enableSync = params.enableSync ?? true // Default to
|
|
41
|
+
let enableSync = params.enableSync ?? true // Default to disabled; callers must explicitly opt in to iCloud sync
|
|
40
42
|
|
|
41
43
|
var query: [String: Any] = [
|
|
42
44
|
kSecClass as String: kSecClassGenericPassword,
|
|
@@ -57,14 +59,40 @@ class KeychainModuleCore {
|
|
|
57
59
|
query[kSecAttrDescription as String] = description
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
//
|
|
61
|
-
SecItemDelete(query as CFDictionary)
|
|
62
|
-
|
|
63
|
-
// Add new item
|
|
62
|
+
// Try to add new item first; if it already exists, update it
|
|
64
63
|
let status = SecItemAdd(query as CFDictionary, nil)
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
if status == errSecDuplicateItem {
|
|
66
|
+
// Item exists - update it instead of delete+add (avoids race condition window)
|
|
67
|
+
let searchQuery: [String: Any] = [
|
|
68
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
69
|
+
kSecAttrService as String: KeychainConstants.serviceIdentifier ?? "",
|
|
70
|
+
kSecAttrAccount as String: params.key,
|
|
71
|
+
kSecAttrSynchronizable as String: kSecAttrSynchronizableAny
|
|
72
|
+
]
|
|
73
|
+
var updateAttrs: [String: Any] = [
|
|
74
|
+
kSecValueData as String: valueData,
|
|
75
|
+
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked,
|
|
76
|
+
kSecAttrSynchronizable as String: enableSync
|
|
77
|
+
]
|
|
78
|
+
if let label = params.label {
|
|
79
|
+
updateAttrs[kSecAttrLabel as String] = label
|
|
80
|
+
}
|
|
81
|
+
if let description = params.description {
|
|
82
|
+
updateAttrs[kSecAttrDescription as String] = description
|
|
83
|
+
}
|
|
84
|
+
let updateStatus = SecItemUpdate(searchQuery as CFDictionary, updateAttrs as CFDictionary)
|
|
85
|
+
guard updateStatus == errSecSuccess else {
|
|
86
|
+
OneKeyLog.error("Keychain", "setItem update: failed, OSStatus: \(updateStatus)")
|
|
87
|
+
throw KeychainModuleError.operationFailed(updateStatus)
|
|
88
|
+
}
|
|
89
|
+
OneKeyLog.info("Keychain", "setItem: updated existing")
|
|
90
|
+
} else {
|
|
91
|
+
guard status == errSecSuccess else {
|
|
92
|
+
OneKeyLog.error("Keychain", "setItem: failed, OSStatus: \(status)")
|
|
93
|
+
throw KeychainModuleError.operationFailed(status)
|
|
94
|
+
}
|
|
95
|
+
OneKeyLog.info("Keychain", "setItem: success")
|
|
68
96
|
}
|
|
69
97
|
}
|
|
70
98
|
|
|
@@ -86,12 +114,15 @@ class KeychainModuleCore {
|
|
|
86
114
|
if status == errSecSuccess {
|
|
87
115
|
if let valueData = result as? Data,
|
|
88
116
|
let value = String(data: valueData, encoding: .utf8) {
|
|
117
|
+
OneKeyLog.debug("Keychain", "getItem: found")
|
|
89
118
|
return GetItemResult(key: params.key, value: value)
|
|
90
119
|
}
|
|
91
120
|
return nil
|
|
92
121
|
} else if status == errSecItemNotFound {
|
|
122
|
+
OneKeyLog.debug("Keychain", "getItem: not found")
|
|
93
123
|
return nil
|
|
94
124
|
} else {
|
|
125
|
+
OneKeyLog.error("Keychain", "getItem: failed, OSStatus: \(status)")
|
|
95
126
|
throw KeychainModuleError.operationFailed(status)
|
|
96
127
|
}
|
|
97
128
|
}
|
|
@@ -110,8 +141,10 @@ class KeychainModuleCore {
|
|
|
110
141
|
|
|
111
142
|
// Both success and item not found are acceptable for delete
|
|
112
143
|
guard status == errSecSuccess || status == errSecItemNotFound else {
|
|
144
|
+
OneKeyLog.error("Keychain", "removeItem: failed, OSStatus: \(status)")
|
|
113
145
|
throw KeychainModuleError.operationFailed(status)
|
|
114
146
|
}
|
|
147
|
+
OneKeyLog.info("Keychain", "removeItem: success")
|
|
115
148
|
}
|
|
116
149
|
|
|
117
150
|
// MARK: - Check Item Existence
|
|
@@ -126,6 +159,7 @@ class KeychainModuleCore {
|
|
|
126
159
|
]
|
|
127
160
|
|
|
128
161
|
let status = SecItemCopyMatching(query as CFDictionary, nil)
|
|
162
|
+
OneKeyLog.debug("Keychain", "hasItem: \(status == errSecSuccess)")
|
|
129
163
|
return status == errSecSuccess
|
|
130
164
|
}
|
|
131
165
|
|
|
@@ -169,6 +203,8 @@ class KeychainModuleCore {
|
|
|
169
203
|
// Common error codes:
|
|
170
204
|
// errSecMissingEntitlement (-34018): Missing iCloud Keychain entitlement
|
|
171
205
|
// errSecNotAvailable (-25291): iCloud Keychain not available/signed out
|
|
172
|
-
|
|
206
|
+
let enabled = addStatus == errSecSuccess
|
|
207
|
+
OneKeyLog.info("Keychain", "iCloud sync check result: \(enabled)")
|
|
208
|
+
return enabled
|
|
173
209
|
}
|
|
174
210
|
}
|