react-native-nitro-storage 0.3.0 → 0.3.1
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/README.md +414 -256
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +98 -11
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +15 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +130 -33
- package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
- package/cpp/bindings/HybridStorage.cpp +121 -12
- package/cpp/bindings/HybridStorage.hpp +10 -0
- package/cpp/core/NativeStorageAdapter.hpp +15 -0
- package/ios/IOSStorageAdapterCpp.hpp +19 -0
- package/ios/IOSStorageAdapterCpp.mm +233 -32
- package/lib/commonjs/Storage.types.js +23 -1
- package/lib/commonjs/Storage.types.js.map +1 -1
- package/lib/commonjs/index.js +173 -32
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +289 -49
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/internal.js +10 -0
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/module/Storage.types.js +22 -0
- package/lib/module/Storage.types.js.map +1 -1
- package/lib/module/index.js +163 -35
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +278 -51
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/internal.js +8 -0
- package/lib/module/internal.js.map +1 -1
- package/lib/typescript/Storage.nitro.d.ts +10 -0
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +20 -0
- package/lib/typescript/Storage.types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +30 -7
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +40 -7
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/internal.d.ts +2 -0
- package/lib/typescript/internal.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +10 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +10 -0
- package/package.json +4 -1
- package/src/Storage.nitro.ts +11 -2
- package/src/Storage.types.ts +22 -0
- package/src/index.ts +270 -71
- package/src/index.web.ts +431 -90
- package/src/internal.ts +14 -4
- package/src/migration.ts +1 -1
|
@@ -16,13 +16,11 @@ local_ref<JavaStringArray> toJavaStringArray(const std::vector<std::string>& val
|
|
|
16
16
|
return javaArray;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
std::vector<std::optional<std::string>>
|
|
19
|
+
std::vector<std::optional<std::string>> fromNullableJavaStringArray(alias_ref<JavaStringArray> values) {
|
|
20
20
|
std::vector<std::optional<std::string>> parsedValues;
|
|
21
|
-
if (!values)
|
|
22
|
-
return parsedValues;
|
|
23
|
-
}
|
|
21
|
+
if (!values) return parsedValues;
|
|
24
22
|
|
|
25
|
-
const
|
|
23
|
+
const jsize size = static_cast<jsize>(values->size());
|
|
26
24
|
parsedValues.reserve(size);
|
|
27
25
|
for (jsize i = 0; i < size; ++i) {
|
|
28
26
|
auto currentValue = values->getElement(i);
|
|
@@ -35,6 +33,21 @@ std::vector<std::optional<std::string>> fromJavaStringArray(alias_ref<JavaString
|
|
|
35
33
|
return parsedValues;
|
|
36
34
|
}
|
|
37
35
|
|
|
36
|
+
std::vector<std::string> fromJavaStringArray(alias_ref<JavaStringArray> values) {
|
|
37
|
+
std::vector<std::string> result;
|
|
38
|
+
if (!values) return result;
|
|
39
|
+
|
|
40
|
+
const jsize size = static_cast<jsize>(values->size());
|
|
41
|
+
result.reserve(size);
|
|
42
|
+
for (jsize i = 0; i < size; ++i) {
|
|
43
|
+
auto currentValue = values->getElement(i);
|
|
44
|
+
if (currentValue) {
|
|
45
|
+
result.push_back(currentValue->toStdString());
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
} // namespace
|
|
39
52
|
|
|
40
53
|
AndroidStorageAdapterCpp::AndroidStorageAdapterCpp(alias_ref<JObject> context) {
|
|
@@ -45,6 +58,8 @@ AndroidStorageAdapterCpp::AndroidStorageAdapterCpp(alias_ref<JObject> context) {
|
|
|
45
58
|
|
|
46
59
|
AndroidStorageAdapterCpp::~AndroidStorageAdapterCpp() = default;
|
|
47
60
|
|
|
61
|
+
// --- Disk ---
|
|
62
|
+
|
|
48
63
|
void AndroidStorageAdapterCpp::setDisk(const std::string& key, const std::string& value) {
|
|
49
64
|
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setDisk");
|
|
50
65
|
method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
|
|
@@ -62,6 +77,24 @@ void AndroidStorageAdapterCpp::deleteDisk(const std::string& key) {
|
|
|
62
77
|
method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
63
78
|
}
|
|
64
79
|
|
|
80
|
+
bool AndroidStorageAdapterCpp::hasDisk(const std::string& key) {
|
|
81
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jboolean(std::string)>("hasDisk");
|
|
82
|
+
return method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
std::vector<std::string> AndroidStorageAdapterCpp::getAllKeysDisk() {
|
|
86
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<
|
|
87
|
+
local_ref<JavaStringArray>()
|
|
88
|
+
>("getAllKeysDisk");
|
|
89
|
+
auto keys = method(AndroidStorageAdapterJava::javaClassStatic());
|
|
90
|
+
return fromJavaStringArray(keys);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
size_t AndroidStorageAdapterCpp::sizeDisk() {
|
|
94
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jint()>("sizeDisk");
|
|
95
|
+
return static_cast<size_t>(method(AndroidStorageAdapterJava::javaClassStatic()));
|
|
96
|
+
}
|
|
97
|
+
|
|
65
98
|
void AndroidStorageAdapterCpp::setDiskBatch(
|
|
66
99
|
const std::vector<std::string>& keys,
|
|
67
100
|
const std::vector<std::string>& values
|
|
@@ -84,7 +117,7 @@ std::vector<std::optional<std::string>> AndroidStorageAdapterCpp::getDiskBatch(
|
|
|
84
117
|
local_ref<JavaStringArray>(alias_ref<JavaStringArray>)
|
|
85
118
|
>("getDiskBatch");
|
|
86
119
|
auto values = method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
|
|
87
|
-
return
|
|
120
|
+
return fromNullableJavaStringArray(values);
|
|
88
121
|
}
|
|
89
122
|
|
|
90
123
|
void AndroidStorageAdapterCpp::deleteDiskBatch(const std::vector<std::string>& keys) {
|
|
@@ -96,6 +129,13 @@ void AndroidStorageAdapterCpp::deleteDiskBatch(const std::vector<std::string>& k
|
|
|
96
129
|
method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
|
|
97
130
|
}
|
|
98
131
|
|
|
132
|
+
void AndroidStorageAdapterCpp::clearDisk() {
|
|
133
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearDisk");
|
|
134
|
+
method(AndroidStorageAdapterJava::javaClassStatic());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// --- Secure ---
|
|
138
|
+
|
|
99
139
|
void AndroidStorageAdapterCpp::setSecure(const std::string& key, const std::string& value) {
|
|
100
140
|
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setSecure");
|
|
101
141
|
method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
|
|
@@ -113,6 +153,24 @@ void AndroidStorageAdapterCpp::deleteSecure(const std::string& key) {
|
|
|
113
153
|
method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
114
154
|
}
|
|
115
155
|
|
|
156
|
+
bool AndroidStorageAdapterCpp::hasSecure(const std::string& key) {
|
|
157
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jboolean(std::string)>("hasSecure");
|
|
158
|
+
return method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
std::vector<std::string> AndroidStorageAdapterCpp::getAllKeysSecure() {
|
|
162
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<
|
|
163
|
+
local_ref<JavaStringArray>()
|
|
164
|
+
>("getAllKeysSecure");
|
|
165
|
+
auto keys = method(AndroidStorageAdapterJava::javaClassStatic());
|
|
166
|
+
return fromJavaStringArray(keys);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
size_t AndroidStorageAdapterCpp::sizeSecure() {
|
|
170
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jint()>("sizeSecure");
|
|
171
|
+
return static_cast<size_t>(method(AndroidStorageAdapterJava::javaClassStatic()));
|
|
172
|
+
}
|
|
173
|
+
|
|
116
174
|
void AndroidStorageAdapterCpp::setSecureBatch(
|
|
117
175
|
const std::vector<std::string>& keys,
|
|
118
176
|
const std::vector<std::string>& values
|
|
@@ -135,7 +193,7 @@ std::vector<std::optional<std::string>> AndroidStorageAdapterCpp::getSecureBatch
|
|
|
135
193
|
local_ref<JavaStringArray>(alias_ref<JavaStringArray>)
|
|
136
194
|
>("getSecureBatch");
|
|
137
195
|
auto values = method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
|
|
138
|
-
return
|
|
196
|
+
return fromNullableJavaStringArray(values);
|
|
139
197
|
}
|
|
140
198
|
|
|
141
199
|
void AndroidStorageAdapterCpp::deleteSecureBatch(const std::vector<std::string>& keys) {
|
|
@@ -147,13 +205,42 @@ void AndroidStorageAdapterCpp::deleteSecureBatch(const std::vector<std::string>&
|
|
|
147
205
|
method(AndroidStorageAdapterJava::javaClassStatic(), javaKeys);
|
|
148
206
|
}
|
|
149
207
|
|
|
150
|
-
void AndroidStorageAdapterCpp::
|
|
151
|
-
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("
|
|
208
|
+
void AndroidStorageAdapterCpp::clearSecure() {
|
|
209
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearSecure");
|
|
152
210
|
method(AndroidStorageAdapterJava::javaClassStatic());
|
|
153
211
|
}
|
|
154
212
|
|
|
155
|
-
|
|
156
|
-
|
|
213
|
+
// --- Config (no-ops on Android; access control / groups are iOS-specific) ---
|
|
214
|
+
|
|
215
|
+
void AndroidStorageAdapterCpp::setSecureAccessControl(int /*level*/) {}
|
|
216
|
+
void AndroidStorageAdapterCpp::setKeychainAccessGroup(const std::string& /*group*/) {}
|
|
217
|
+
|
|
218
|
+
// --- Biometric ---
|
|
219
|
+
|
|
220
|
+
void AndroidStorageAdapterCpp::setSecureBiometric(const std::string& key, const std::string& value) {
|
|
221
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setSecureBiometric");
|
|
222
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
std::optional<std::string> AndroidStorageAdapterCpp::getSecureBiometric(const std::string& key) {
|
|
226
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jstring(std::string)>("getSecureBiometric");
|
|
227
|
+
auto result = method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
228
|
+
if (!result) return std::nullopt;
|
|
229
|
+
return result->toStdString();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
void AndroidStorageAdapterCpp::deleteSecureBiometric(const std::string& key) {
|
|
233
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string)>("deleteSecureBiometric");
|
|
234
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
bool AndroidStorageAdapterCpp::hasSecureBiometric(const std::string& key) {
|
|
238
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jboolean(std::string)>("hasSecureBiometric");
|
|
239
|
+
return method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
void AndroidStorageAdapterCpp::clearSecureBiometric() {
|
|
243
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearSecureBiometric");
|
|
157
244
|
method(AndroidStorageAdapterJava::javaClassStatic());
|
|
158
245
|
}
|
|
159
246
|
|
|
@@ -28,6 +28,9 @@ public:
|
|
|
28
28
|
void setDisk(const std::string& key, const std::string& value) override;
|
|
29
29
|
std::optional<std::string> getDisk(const std::string& key) override;
|
|
30
30
|
void deleteDisk(const std::string& key) override;
|
|
31
|
+
bool hasDisk(const std::string& key) override;
|
|
32
|
+
std::vector<std::string> getAllKeysDisk() override;
|
|
33
|
+
size_t sizeDisk() override;
|
|
31
34
|
void setDiskBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
|
|
32
35
|
std::vector<std::optional<std::string>> getDiskBatch(const std::vector<std::string>& keys) override;
|
|
33
36
|
void deleteDiskBatch(const std::vector<std::string>& keys) override;
|
|
@@ -35,12 +38,24 @@ public:
|
|
|
35
38
|
void setSecure(const std::string& key, const std::string& value) override;
|
|
36
39
|
std::optional<std::string> getSecure(const std::string& key) override;
|
|
37
40
|
void deleteSecure(const std::string& key) override;
|
|
41
|
+
bool hasSecure(const std::string& key) override;
|
|
42
|
+
std::vector<std::string> getAllKeysSecure() override;
|
|
43
|
+
size_t sizeSecure() override;
|
|
38
44
|
void setSecureBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values) override;
|
|
39
45
|
std::vector<std::optional<std::string>> getSecureBatch(const std::vector<std::string>& keys) override;
|
|
40
46
|
void deleteSecureBatch(const std::vector<std::string>& keys) override;
|
|
41
47
|
|
|
42
48
|
void clearDisk() override;
|
|
43
49
|
void clearSecure() override;
|
|
50
|
+
|
|
51
|
+
void setSecureAccessControl(int level) override;
|
|
52
|
+
void setKeychainAccessGroup(const std::string& group) override;
|
|
53
|
+
|
|
54
|
+
void setSecureBiometric(const std::string& key, const std::string& value) override;
|
|
55
|
+
std::optional<std::string> getSecureBiometric(const std::string& key) override;
|
|
56
|
+
void deleteSecureBiometric(const std::string& key) override;
|
|
57
|
+
bool hasSecureBiometric(const std::string& key) override;
|
|
58
|
+
void clearSecureBiometric() override;
|
|
44
59
|
};
|
|
45
60
|
|
|
46
61
|
} // namespace NitroStorage
|
|
@@ -17,34 +17,46 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
17
17
|
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
18
18
|
.build()
|
|
19
19
|
|
|
20
|
-
private val encryptedPreferences: SharedPreferences = initializeEncryptedPreferences()
|
|
20
|
+
private val encryptedPreferences: SharedPreferences = initializeEncryptedPreferences("NitroStorageSecure", masterKey)
|
|
21
|
+
|
|
22
|
+
private val biometricMasterKeyAlias = "${context.packageName}.nitro_storage.biometric_key"
|
|
23
|
+
|
|
24
|
+
private val biometricPreferences: SharedPreferences by lazy {
|
|
25
|
+
try {
|
|
26
|
+
val bioKey = MasterKey.Builder(context, biometricMasterKeyAlias)
|
|
27
|
+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
28
|
+
.setUserAuthenticationRequired(true, 30)
|
|
29
|
+
.build()
|
|
30
|
+
initializeEncryptedPreferences("NitroStorageBiometric", bioKey)
|
|
31
|
+
} catch (e: Exception) {
|
|
32
|
+
Log.w("NitroStorage", "Biometric storage unavailable, falling back to regular encrypted storage: ${e.message}")
|
|
33
|
+
encryptedPreferences
|
|
34
|
+
}
|
|
35
|
+
}
|
|
21
36
|
|
|
22
|
-
private fun initializeEncryptedPreferences(): SharedPreferences {
|
|
37
|
+
private fun initializeEncryptedPreferences(name: String, key: MasterKey): SharedPreferences {
|
|
23
38
|
return try {
|
|
24
39
|
EncryptedSharedPreferences.create(
|
|
25
40
|
context,
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
name,
|
|
42
|
+
key,
|
|
28
43
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
29
44
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
30
45
|
)
|
|
31
46
|
} catch (e: Exception) {
|
|
32
|
-
// Handle corrupted keystore keys by clearing and re-initializing
|
|
33
47
|
if (e is AEADBadTagException || e.cause is AEADBadTagException) {
|
|
34
|
-
Log.w("NitroStorage", "Detected corrupted encryption keys, clearing
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Retry initialization
|
|
48
|
+
Log.w("NitroStorage", "Detected corrupted encryption keys for $name, clearing...")
|
|
49
|
+
clearCorruptedStorage(name, key)
|
|
38
50
|
EncryptedSharedPreferences.create(
|
|
39
51
|
context,
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
name,
|
|
53
|
+
key,
|
|
42
54
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
43
55
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
44
56
|
)
|
|
45
57
|
} else {
|
|
46
58
|
throw RuntimeException(
|
|
47
|
-
"NitroStorage: Failed to initialize
|
|
59
|
+
"NitroStorage: Failed to initialize $name. " +
|
|
48
60
|
"This may be due to corrupted encryption keys. " +
|
|
49
61
|
"Try clearing app data or reinstalling the app.", e
|
|
50
62
|
)
|
|
@@ -52,19 +64,30 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
66
|
|
|
55
|
-
private fun
|
|
67
|
+
private fun clearCorruptedStorage(name: String, key: MasterKey) {
|
|
56
68
|
try {
|
|
57
|
-
|
|
58
|
-
context.deleteSharedPreferences("NitroStorageSecure")
|
|
59
|
-
|
|
60
|
-
// Delete the master key from Android Keystore
|
|
69
|
+
context.deleteSharedPreferences(name)
|
|
61
70
|
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
|
62
71
|
keyStore.load(null)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Log.i("NitroStorage", "
|
|
72
|
+
val alias = if (key === masterKey) masterKeyAlias else biometricMasterKeyAlias
|
|
73
|
+
keyStore.deleteEntry(alias)
|
|
74
|
+
Log.i("NitroStorage", "Cleared corrupted storage: $name")
|
|
66
75
|
} catch (e: Exception) {
|
|
67
|
-
Log.e("NitroStorage", "Failed to clear corrupted
|
|
76
|
+
Log.e("NitroStorage", "Failed to clear corrupted storage: $name", e)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun getSecureSafe(prefs: SharedPreferences, key: String): String? {
|
|
81
|
+
return try {
|
|
82
|
+
prefs.getString(key, null)
|
|
83
|
+
} catch (e: Exception) {
|
|
84
|
+
if (e is AEADBadTagException || e.cause is AEADBadTagException) {
|
|
85
|
+
Log.w("NitroStorage", "Corrupt entry for key '$key', removing")
|
|
86
|
+
prefs.edit().remove(key).commit()
|
|
87
|
+
null
|
|
88
|
+
} else {
|
|
89
|
+
throw e
|
|
90
|
+
}
|
|
68
91
|
}
|
|
69
92
|
}
|
|
70
93
|
|
|
@@ -94,6 +117,8 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
94
117
|
fun getContext(): Context {
|
|
95
118
|
return getInstanceOrThrow().context
|
|
96
119
|
}
|
|
120
|
+
|
|
121
|
+
// --- Disk ---
|
|
97
122
|
|
|
98
123
|
@JvmStatic
|
|
99
124
|
fun setDisk(key: String, value: String) {
|
|
@@ -136,10 +161,32 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
136
161
|
}
|
|
137
162
|
editor.apply()
|
|
138
163
|
}
|
|
164
|
+
|
|
165
|
+
@JvmStatic
|
|
166
|
+
fun hasDisk(key: String): Boolean {
|
|
167
|
+
return getInstanceOrThrow().sharedPreferences.contains(key)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@JvmStatic
|
|
171
|
+
fun getAllKeysDisk(): Array<String> {
|
|
172
|
+
return getInstanceOrThrow().sharedPreferences.all.keys.toTypedArray()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@JvmStatic
|
|
176
|
+
fun sizeDisk(): Int {
|
|
177
|
+
return getInstanceOrThrow().sharedPreferences.all.size
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@JvmStatic
|
|
181
|
+
fun clearDisk() {
|
|
182
|
+
getInstanceOrThrow().sharedPreferences.edit().clear().apply()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// --- Secure (uses commit for reliability) ---
|
|
139
186
|
|
|
140
187
|
@JvmStatic
|
|
141
188
|
fun setSecure(key: String, value: String) {
|
|
142
|
-
getInstanceOrThrow().encryptedPreferences.edit().putString(key, value).
|
|
189
|
+
getInstanceOrThrow().encryptedPreferences.edit().putString(key, value).commit()
|
|
143
190
|
}
|
|
144
191
|
|
|
145
192
|
@JvmStatic
|
|
@@ -149,44 +196,94 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
149
196
|
for (index in 0 until count) {
|
|
150
197
|
editor.putString(keys[index], values[index])
|
|
151
198
|
}
|
|
152
|
-
editor.
|
|
199
|
+
editor.commit()
|
|
153
200
|
}
|
|
154
201
|
|
|
155
202
|
@JvmStatic
|
|
156
203
|
fun getSecure(key: String): String? {
|
|
157
|
-
return getInstanceOrThrow().encryptedPreferences
|
|
204
|
+
return getInstanceOrThrow().getSecureSafe(getInstanceOrThrow().encryptedPreferences, key)
|
|
158
205
|
}
|
|
159
206
|
|
|
160
207
|
@JvmStatic
|
|
161
208
|
fun getSecureBatch(keys: Array<String>): Array<String?> {
|
|
162
|
-
val
|
|
209
|
+
val inst = getInstanceOrThrow()
|
|
163
210
|
return Array(keys.size) { index ->
|
|
164
|
-
|
|
211
|
+
inst.getSecureSafe(inst.encryptedPreferences, keys[index])
|
|
165
212
|
}
|
|
166
213
|
}
|
|
167
214
|
|
|
168
215
|
@JvmStatic
|
|
169
216
|
fun deleteSecure(key: String) {
|
|
170
|
-
getInstanceOrThrow()
|
|
217
|
+
val inst = getInstanceOrThrow()
|
|
218
|
+
inst.encryptedPreferences.edit().remove(key).commit()
|
|
219
|
+
inst.biometricPreferences.edit().remove(key).commit()
|
|
171
220
|
}
|
|
172
221
|
|
|
173
222
|
@JvmStatic
|
|
174
223
|
fun deleteSecureBatch(keys: Array<String>) {
|
|
175
|
-
val
|
|
224
|
+
val inst = getInstanceOrThrow()
|
|
225
|
+
val secureEditor = inst.encryptedPreferences.edit()
|
|
226
|
+
val biometricEditor = inst.biometricPreferences.edit()
|
|
176
227
|
for (key in keys) {
|
|
177
|
-
|
|
228
|
+
secureEditor.remove(key)
|
|
229
|
+
biometricEditor.remove(key)
|
|
178
230
|
}
|
|
179
|
-
|
|
231
|
+
secureEditor.commit()
|
|
232
|
+
biometricEditor.commit()
|
|
180
233
|
}
|
|
181
234
|
|
|
182
235
|
@JvmStatic
|
|
183
|
-
fun
|
|
184
|
-
getInstanceOrThrow()
|
|
236
|
+
fun hasSecure(key: String): Boolean {
|
|
237
|
+
val inst = getInstanceOrThrow()
|
|
238
|
+
return inst.encryptedPreferences.contains(key) || inst.biometricPreferences.contains(key)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@JvmStatic
|
|
242
|
+
fun getAllKeysSecure(): Array<String> {
|
|
243
|
+
val inst = getInstanceOrThrow()
|
|
244
|
+
val keys = linkedSetOf<String>()
|
|
245
|
+
keys.addAll(inst.encryptedPreferences.all.keys)
|
|
246
|
+
keys.addAll(inst.biometricPreferences.all.keys)
|
|
247
|
+
return keys.toTypedArray()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@JvmStatic
|
|
251
|
+
fun sizeSecure(): Int {
|
|
252
|
+
return getAllKeysSecure().size
|
|
185
253
|
}
|
|
186
254
|
|
|
187
255
|
@JvmStatic
|
|
188
256
|
fun clearSecure() {
|
|
189
|
-
getInstanceOrThrow()
|
|
257
|
+
val inst = getInstanceOrThrow()
|
|
258
|
+
inst.encryptedPreferences.edit().clear().commit()
|
|
259
|
+
inst.biometricPreferences.edit().clear().commit()
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// --- Biometric (separate encrypted store, requires recent biometric auth on Android) ---
|
|
263
|
+
|
|
264
|
+
@JvmStatic
|
|
265
|
+
fun setSecureBiometric(key: String, value: String) {
|
|
266
|
+
getInstanceOrThrow().biometricPreferences.edit().putString(key, value).commit()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@JvmStatic
|
|
270
|
+
fun getSecureBiometric(key: String): String? {
|
|
271
|
+
return getInstanceOrThrow().getSecureSafe(getInstanceOrThrow().biometricPreferences, key)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@JvmStatic
|
|
275
|
+
fun deleteSecureBiometric(key: String) {
|
|
276
|
+
getInstanceOrThrow().biometricPreferences.edit().remove(key).commit()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@JvmStatic
|
|
280
|
+
fun hasSecureBiometric(key: String): Boolean {
|
|
281
|
+
return getInstanceOrThrow().biometricPreferences.contains(key)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@JvmStatic
|
|
285
|
+
fun clearSecureBiometric() {
|
|
286
|
+
getInstanceOrThrow().biometricPreferences.edit().clear().commit()
|
|
190
287
|
}
|
|
191
288
|
}
|
|
192
289
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
package com.nitrostorage
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
6
|
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
7
|
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
8
|
import java.util.HashMap
|
|
9
9
|
|
|
10
|
-
class NitroStoragePackage :
|
|
10
|
+
class NitroStoragePackage : BaseReactPackage() {
|
|
11
11
|
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
12
|
return null
|
|
13
13
|
}
|
|
@@ -143,6 +143,65 @@ void HybridStorage::remove(const std::string& key, double scope) {
|
|
|
143
143
|
notifyListeners(static_cast<int>(s), key, std::nullopt);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
bool HybridStorage::has(const std::string& key, double scope) {
|
|
147
|
+
Scope s = toScope(scope);
|
|
148
|
+
|
|
149
|
+
switch (s) {
|
|
150
|
+
case Scope::Memory: {
|
|
151
|
+
std::lock_guard<std::mutex> lock(memoryMutex_);
|
|
152
|
+
return memoryStore_.find(key) != memoryStore_.end();
|
|
153
|
+
}
|
|
154
|
+
case Scope::Disk:
|
|
155
|
+
ensureAdapter();
|
|
156
|
+
return nativeAdapter_->hasDisk(key);
|
|
157
|
+
case Scope::Secure:
|
|
158
|
+
ensureAdapter();
|
|
159
|
+
return nativeAdapter_->hasSecure(key);
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
std::vector<std::string> HybridStorage::getAllKeys(double scope) {
|
|
165
|
+
Scope s = toScope(scope);
|
|
166
|
+
|
|
167
|
+
switch (s) {
|
|
168
|
+
case Scope::Memory: {
|
|
169
|
+
std::lock_guard<std::mutex> lock(memoryMutex_);
|
|
170
|
+
std::vector<std::string> keys;
|
|
171
|
+
keys.reserve(memoryStore_.size());
|
|
172
|
+
for (const auto& pair : memoryStore_) {
|
|
173
|
+
keys.push_back(pair.first);
|
|
174
|
+
}
|
|
175
|
+
return keys;
|
|
176
|
+
}
|
|
177
|
+
case Scope::Disk:
|
|
178
|
+
ensureAdapter();
|
|
179
|
+
return nativeAdapter_->getAllKeysDisk();
|
|
180
|
+
case Scope::Secure:
|
|
181
|
+
ensureAdapter();
|
|
182
|
+
return nativeAdapter_->getAllKeysSecure();
|
|
183
|
+
}
|
|
184
|
+
return {};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
double HybridStorage::size(double scope) {
|
|
188
|
+
Scope s = toScope(scope);
|
|
189
|
+
|
|
190
|
+
switch (s) {
|
|
191
|
+
case Scope::Memory: {
|
|
192
|
+
std::lock_guard<std::mutex> lock(memoryMutex_);
|
|
193
|
+
return static_cast<double>(memoryStore_.size());
|
|
194
|
+
}
|
|
195
|
+
case Scope::Disk:
|
|
196
|
+
ensureAdapter();
|
|
197
|
+
return static_cast<double>(nativeAdapter_->sizeDisk());
|
|
198
|
+
case Scope::Secure:
|
|
199
|
+
ensureAdapter();
|
|
200
|
+
return static_cast<double>(nativeAdapter_->sizeSecure());
|
|
201
|
+
}
|
|
202
|
+
return 0.0;
|
|
203
|
+
}
|
|
204
|
+
|
|
146
205
|
std::function<void()> HybridStorage::addOnChange(
|
|
147
206
|
double scope,
|
|
148
207
|
const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
|
|
@@ -244,7 +303,6 @@ void HybridStorage::setBatch(const std::vector<std::string>& keys, const std::ve
|
|
|
244
303
|
}
|
|
245
304
|
}
|
|
246
305
|
|
|
247
|
-
|
|
248
306
|
std::vector<std::string> HybridStorage::getBatch(const std::vector<std::string>& keys, double scope) {
|
|
249
307
|
std::vector<std::string> results;
|
|
250
308
|
results.reserve(keys.size());
|
|
@@ -276,11 +334,7 @@ std::vector<std::string> HybridStorage::getBatch(const std::vector<std::string>&
|
|
|
276
334
|
}
|
|
277
335
|
|
|
278
336
|
for (const auto& value : values) {
|
|
279
|
-
|
|
280
|
-
results.push_back(*value);
|
|
281
|
-
} else {
|
|
282
|
-
results.push_back(kBatchMissingSentinel);
|
|
283
|
-
}
|
|
337
|
+
results.push_back(value.has_value() ? *value : std::string(kBatchMissingSentinel));
|
|
284
338
|
}
|
|
285
339
|
return results;
|
|
286
340
|
}
|
|
@@ -296,11 +350,7 @@ std::vector<std::string> HybridStorage::getBatch(const std::vector<std::string>&
|
|
|
296
350
|
}
|
|
297
351
|
|
|
298
352
|
for (const auto& value : values) {
|
|
299
|
-
|
|
300
|
-
results.push_back(*value);
|
|
301
|
-
} else {
|
|
302
|
-
results.push_back(kBatchMissingSentinel);
|
|
303
|
-
}
|
|
353
|
+
results.push_back(value.has_value() ? *value : std::string(kBatchMissingSentinel));
|
|
304
354
|
}
|
|
305
355
|
return results;
|
|
306
356
|
}
|
|
@@ -309,7 +359,6 @@ std::vector<std::string> HybridStorage::getBatch(const std::vector<std::string>&
|
|
|
309
359
|
return results;
|
|
310
360
|
}
|
|
311
361
|
|
|
312
|
-
|
|
313
362
|
void HybridStorage::removeBatch(const std::vector<std::string>& keys, double scope) {
|
|
314
363
|
Scope s = toScope(scope);
|
|
315
364
|
|
|
@@ -348,6 +397,66 @@ void HybridStorage::removeBatch(const std::vector<std::string>& keys, double sco
|
|
|
348
397
|
}
|
|
349
398
|
}
|
|
350
399
|
|
|
400
|
+
// --- Configuration ---
|
|
401
|
+
|
|
402
|
+
void HybridStorage::setSecureAccessControl(double level) {
|
|
403
|
+
ensureAdapter();
|
|
404
|
+
nativeAdapter_->setSecureAccessControl(static_cast<int>(level));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
void HybridStorage::setKeychainAccessGroup(const std::string& group) {
|
|
408
|
+
ensureAdapter();
|
|
409
|
+
nativeAdapter_->setKeychainAccessGroup(group);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// --- Biometric ---
|
|
413
|
+
|
|
414
|
+
void HybridStorage::setSecureBiometric(const std::string& key, const std::string& value) {
|
|
415
|
+
ensureAdapter();
|
|
416
|
+
try {
|
|
417
|
+
nativeAdapter_->setSecureBiometric(key, value);
|
|
418
|
+
notifyListeners(static_cast<int>(Scope::Secure), key, value);
|
|
419
|
+
} catch (const std::exception& e) {
|
|
420
|
+
throw std::runtime_error(std::string("NitroStorage: Biometric set failed: ") + e.what());
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
std::optional<std::string> HybridStorage::getSecureBiometric(const std::string& key) {
|
|
425
|
+
ensureAdapter();
|
|
426
|
+
try {
|
|
427
|
+
return nativeAdapter_->getSecureBiometric(key);
|
|
428
|
+
} catch (const std::exception& e) {
|
|
429
|
+
throw std::runtime_error(std::string("NitroStorage: Biometric get failed: ") + e.what());
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
void HybridStorage::deleteSecureBiometric(const std::string& key) {
|
|
434
|
+
ensureAdapter();
|
|
435
|
+
try {
|
|
436
|
+
nativeAdapter_->deleteSecureBiometric(key);
|
|
437
|
+
notifyListeners(static_cast<int>(Scope::Secure), key, std::nullopt);
|
|
438
|
+
} catch (const std::exception& e) {
|
|
439
|
+
throw std::runtime_error(std::string("NitroStorage: Biometric delete failed: ") + e.what());
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
bool HybridStorage::hasSecureBiometric(const std::string& key) {
|
|
444
|
+
ensureAdapter();
|
|
445
|
+
return nativeAdapter_->hasSecureBiometric(key);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
void HybridStorage::clearSecureBiometric() {
|
|
449
|
+
ensureAdapter();
|
|
450
|
+
try {
|
|
451
|
+
nativeAdapter_->clearSecureBiometric();
|
|
452
|
+
notifyListeners(static_cast<int>(Scope::Secure), "", std::nullopt);
|
|
453
|
+
} catch (const std::exception& e) {
|
|
454
|
+
throw std::runtime_error(std::string("NitroStorage: Biometric clear failed: ") + e.what());
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// --- Internal ---
|
|
459
|
+
|
|
351
460
|
void HybridStorage::notifyListeners(
|
|
352
461
|
int scope,
|
|
353
462
|
const std::string& key,
|
|
@@ -20,6 +20,9 @@ public:
|
|
|
20
20
|
std::optional<std::string> get(const std::string& key, double scope) override;
|
|
21
21
|
void remove(const std::string& key, double scope) override;
|
|
22
22
|
void clear(double scope) override;
|
|
23
|
+
bool has(const std::string& key, double scope) override;
|
|
24
|
+
std::vector<std::string> getAllKeys(double scope) override;
|
|
25
|
+
double size(double scope) override;
|
|
23
26
|
void setBatch(const std::vector<std::string>& keys, const std::vector<std::string>& values, double scope) override;
|
|
24
27
|
std::vector<std::string> getBatch(const std::vector<std::string>& keys, double scope) override;
|
|
25
28
|
void removeBatch(const std::vector<std::string>& keys, double scope) override;
|
|
@@ -27,6 +30,13 @@ public:
|
|
|
27
30
|
double scope,
|
|
28
31
|
const std::function<void(const std::string&, const std::optional<std::string>&)>& callback
|
|
29
32
|
) override;
|
|
33
|
+
void setSecureAccessControl(double level) override;
|
|
34
|
+
void setKeychainAccessGroup(const std::string& group) override;
|
|
35
|
+
void setSecureBiometric(const std::string& key, const std::string& value) override;
|
|
36
|
+
std::optional<std::string> getSecureBiometric(const std::string& key) override;
|
|
37
|
+
void deleteSecureBiometric(const std::string& key) override;
|
|
38
|
+
bool hasSecureBiometric(const std::string& key) override;
|
|
39
|
+
void clearSecureBiometric() override;
|
|
30
40
|
|
|
31
41
|
private:
|
|
32
42
|
enum class Scope {
|