react-native-nitro-storage 0.3.0 → 0.3.2
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 +594 -247
- package/android/CMakeLists.txt +2 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +102 -11
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +16 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +154 -34
- package/android/src/main/java/com/nitrostorage/NitroStoragePackage.kt +2 -2
- package/cpp/bindings/HybridStorage.cpp +176 -21
- package/cpp/bindings/HybridStorage.hpp +29 -2
- package/cpp/core/NativeStorageAdapter.hpp +16 -0
- package/ios/IOSStorageAdapterCpp.hpp +20 -0
- package/ios/IOSStorageAdapterCpp.mm +239 -32
- package/lib/commonjs/Storage.types.js +23 -1
- package/lib/commonjs/Storage.types.js.map +1 -1
- package/lib/commonjs/index.js +292 -75
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +473 -86
- 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/commonjs/storage-hooks.js +36 -0
- package/lib/commonjs/storage-hooks.js.map +1 -0
- package/lib/module/Storage.types.js +22 -0
- package/lib/module/Storage.types.js.map +1 -1
- package/lib/module/index.js +264 -75
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +445 -86
- 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/module/storage-hooks.js +30 -0
- package/lib/module/storage-hooks.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +12 -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 +33 -10
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +45 -10
- 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/lib/typescript/storage-hooks.d.ts +10 -0
- package/lib/typescript/storage-hooks.d.ts.map +1 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +12 -0
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +12 -0
- package/package.json +8 -3
- package/src/Storage.nitro.ts +13 -2
- package/src/Storage.types.ts +22 -0
- package/src/index.ts +382 -123
- package/src/index.web.ts +618 -134
- package/src/internal.ts +14 -4
- package/src/migration.ts +1 -1
- package/src/storage-hooks.ts +48 -0
package/android/CMakeLists.txt
CHANGED
|
@@ -11,6 +11,8 @@ file(GLOB SOURCES
|
|
|
11
11
|
"../cpp/core/*.cpp"
|
|
12
12
|
"./src/main/cpp/*.cpp"
|
|
13
13
|
)
|
|
14
|
+
# Unit/integration C++ tests define `main()` and must never be linked into the Android shared library.
|
|
15
|
+
list(FILTER SOURCES EXCLUDE REGEX ".*/[^/]*Test\\.cpp$")
|
|
14
16
|
|
|
15
17
|
# 2. Create the library target
|
|
16
18
|
add_library(
|
|
@@ -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,46 @@ 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::setSecureWritesAsync(bool enabled) {
|
|
217
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(jboolean)>("setSecureWritesAsync");
|
|
218
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), enabled);
|
|
219
|
+
}
|
|
220
|
+
void AndroidStorageAdapterCpp::setKeychainAccessGroup(const std::string& /*group*/) {}
|
|
221
|
+
|
|
222
|
+
// --- Biometric ---
|
|
223
|
+
|
|
224
|
+
void AndroidStorageAdapterCpp::setSecureBiometric(const std::string& key, const std::string& value) {
|
|
225
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string, std::string)>("setSecureBiometric");
|
|
226
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key, value);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
std::optional<std::string> AndroidStorageAdapterCpp::getSecureBiometric(const std::string& key) {
|
|
230
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jstring(std::string)>("getSecureBiometric");
|
|
231
|
+
auto result = method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
232
|
+
if (!result) return std::nullopt;
|
|
233
|
+
return result->toStdString();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
void AndroidStorageAdapterCpp::deleteSecureBiometric(const std::string& key) {
|
|
237
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void(std::string)>("deleteSecureBiometric");
|
|
238
|
+
method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
bool AndroidStorageAdapterCpp::hasSecureBiometric(const std::string& key) {
|
|
242
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<jboolean(std::string)>("hasSecureBiometric");
|
|
243
|
+
return method(AndroidStorageAdapterJava::javaClassStatic(), key);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
void AndroidStorageAdapterCpp::clearSecureBiometric() {
|
|
247
|
+
static auto method = AndroidStorageAdapterJava::javaClassStatic()->getStaticMethod<void()>("clearSecureBiometric");
|
|
157
248
|
method(AndroidStorageAdapterJava::javaClassStatic());
|
|
158
249
|
}
|
|
159
250
|
|
|
@@ -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,25 @@ 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 setSecureWritesAsync(bool enabled) override;
|
|
53
|
+
void setKeychainAccessGroup(const std::string& group) override;
|
|
54
|
+
|
|
55
|
+
void setSecureBiometric(const std::string& key, const std::string& value) override;
|
|
56
|
+
std::optional<std::string> getSecureBiometric(const std::string& key) override;
|
|
57
|
+
void deleteSecureBiometric(const std::string& key) override;
|
|
58
|
+
bool hasSecureBiometric(const std::string& key) override;
|
|
59
|
+
void clearSecureBiometric() override;
|
|
44
60
|
};
|
|
45
61
|
|
|
46
62
|
} // namespace NitroStorage
|
|
@@ -17,34 +17,49 @@ 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
|
+
}
|
|
36
|
+
|
|
37
|
+
@Volatile
|
|
38
|
+
private var secureWritesAsync = false
|
|
21
39
|
|
|
22
|
-
private fun initializeEncryptedPreferences(): SharedPreferences {
|
|
40
|
+
private fun initializeEncryptedPreferences(name: String, key: MasterKey): SharedPreferences {
|
|
23
41
|
return try {
|
|
24
42
|
EncryptedSharedPreferences.create(
|
|
25
43
|
context,
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
name,
|
|
45
|
+
key,
|
|
28
46
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
29
47
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
30
48
|
)
|
|
31
49
|
} catch (e: Exception) {
|
|
32
|
-
// Handle corrupted keystore keys by clearing and re-initializing
|
|
33
50
|
if (e is AEADBadTagException || e.cause is AEADBadTagException) {
|
|
34
|
-
Log.w("NitroStorage", "Detected corrupted encryption keys, clearing
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Retry initialization
|
|
51
|
+
Log.w("NitroStorage", "Detected corrupted encryption keys for $name, clearing...")
|
|
52
|
+
clearCorruptedStorage(name, key)
|
|
38
53
|
EncryptedSharedPreferences.create(
|
|
39
54
|
context,
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
name,
|
|
56
|
+
key,
|
|
42
57
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
43
58
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
44
59
|
)
|
|
45
60
|
} else {
|
|
46
61
|
throw RuntimeException(
|
|
47
|
-
"NitroStorage: Failed to initialize
|
|
62
|
+
"NitroStorage: Failed to initialize $name. " +
|
|
48
63
|
"This may be due to corrupted encryption keys. " +
|
|
49
64
|
"Try clearing app data or reinstalling the app.", e
|
|
50
65
|
)
|
|
@@ -52,19 +67,38 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
52
67
|
}
|
|
53
68
|
}
|
|
54
69
|
|
|
55
|
-
private fun
|
|
70
|
+
private fun clearCorruptedStorage(name: String, key: MasterKey) {
|
|
56
71
|
try {
|
|
57
|
-
|
|
58
|
-
context.deleteSharedPreferences("NitroStorageSecure")
|
|
59
|
-
|
|
60
|
-
// Delete the master key from Android Keystore
|
|
72
|
+
context.deleteSharedPreferences(name)
|
|
61
73
|
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
|
62
74
|
keyStore.load(null)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Log.i("NitroStorage", "
|
|
75
|
+
val alias = if (key === masterKey) masterKeyAlias else biometricMasterKeyAlias
|
|
76
|
+
keyStore.deleteEntry(alias)
|
|
77
|
+
Log.i("NitroStorage", "Cleared corrupted storage: $name")
|
|
66
78
|
} catch (e: Exception) {
|
|
67
|
-
Log.e("NitroStorage", "Failed to clear corrupted
|
|
79
|
+
Log.e("NitroStorage", "Failed to clear corrupted storage: $name", e)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private fun getSecureSafe(prefs: SharedPreferences, key: String): String? {
|
|
84
|
+
return try {
|
|
85
|
+
prefs.getString(key, null)
|
|
86
|
+
} catch (e: Exception) {
|
|
87
|
+
if (e is AEADBadTagException || e.cause is AEADBadTagException) {
|
|
88
|
+
Log.w("NitroStorage", "Corrupt entry for key '$key', removing")
|
|
89
|
+
prefs.edit().remove(key).commit()
|
|
90
|
+
null
|
|
91
|
+
} else {
|
|
92
|
+
throw e
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private fun applySecureEditor(editor: SharedPreferences.Editor) {
|
|
98
|
+
if (secureWritesAsync) {
|
|
99
|
+
editor.apply()
|
|
100
|
+
} else {
|
|
101
|
+
editor.commit()
|
|
68
102
|
}
|
|
69
103
|
}
|
|
70
104
|
|
|
@@ -94,6 +128,13 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
94
128
|
fun getContext(): Context {
|
|
95
129
|
return getInstanceOrThrow().context
|
|
96
130
|
}
|
|
131
|
+
|
|
132
|
+
@JvmStatic
|
|
133
|
+
fun setSecureWritesAsync(enabled: Boolean) {
|
|
134
|
+
getInstanceOrThrow().secureWritesAsync = enabled
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// --- Disk ---
|
|
97
138
|
|
|
98
139
|
@JvmStatic
|
|
99
140
|
fun setDisk(key: String, value: String) {
|
|
@@ -136,57 +177,136 @@ class AndroidStorageAdapter private constructor(private val context: Context) {
|
|
|
136
177
|
}
|
|
137
178
|
editor.apply()
|
|
138
179
|
}
|
|
180
|
+
|
|
181
|
+
@JvmStatic
|
|
182
|
+
fun hasDisk(key: String): Boolean {
|
|
183
|
+
return getInstanceOrThrow().sharedPreferences.contains(key)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@JvmStatic
|
|
187
|
+
fun getAllKeysDisk(): Array<String> {
|
|
188
|
+
return getInstanceOrThrow().sharedPreferences.all.keys.toTypedArray()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@JvmStatic
|
|
192
|
+
fun sizeDisk(): Int {
|
|
193
|
+
return getInstanceOrThrow().sharedPreferences.all.size
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@JvmStatic
|
|
197
|
+
fun clearDisk() {
|
|
198
|
+
getInstanceOrThrow().sharedPreferences.edit().clear().apply()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// --- Secure (sync commit by default, async apply when enabled) ---
|
|
139
202
|
|
|
140
203
|
@JvmStatic
|
|
141
204
|
fun setSecure(key: String, value: String) {
|
|
142
|
-
getInstanceOrThrow()
|
|
205
|
+
val inst = getInstanceOrThrow()
|
|
206
|
+
val editor = inst.encryptedPreferences.edit().putString(key, value)
|
|
207
|
+
inst.applySecureEditor(editor)
|
|
143
208
|
}
|
|
144
209
|
|
|
145
210
|
@JvmStatic
|
|
146
211
|
fun setSecureBatch(keys: Array<String>, values: Array<String>) {
|
|
147
|
-
val
|
|
212
|
+
val inst = getInstanceOrThrow()
|
|
213
|
+
val editor = inst.encryptedPreferences.edit()
|
|
148
214
|
val count = minOf(keys.size, values.size)
|
|
149
215
|
for (index in 0 until count) {
|
|
150
216
|
editor.putString(keys[index], values[index])
|
|
151
217
|
}
|
|
152
|
-
|
|
218
|
+
inst.applySecureEditor(editor)
|
|
153
219
|
}
|
|
154
220
|
|
|
155
221
|
@JvmStatic
|
|
156
222
|
fun getSecure(key: String): String? {
|
|
157
|
-
return getInstanceOrThrow().encryptedPreferences
|
|
223
|
+
return getInstanceOrThrow().getSecureSafe(getInstanceOrThrow().encryptedPreferences, key)
|
|
158
224
|
}
|
|
159
225
|
|
|
160
226
|
@JvmStatic
|
|
161
227
|
fun getSecureBatch(keys: Array<String>): Array<String?> {
|
|
162
|
-
val
|
|
228
|
+
val inst = getInstanceOrThrow()
|
|
163
229
|
return Array(keys.size) { index ->
|
|
164
|
-
|
|
230
|
+
inst.getSecureSafe(inst.encryptedPreferences, keys[index])
|
|
165
231
|
}
|
|
166
232
|
}
|
|
167
233
|
|
|
168
234
|
@JvmStatic
|
|
169
235
|
fun deleteSecure(key: String) {
|
|
170
|
-
getInstanceOrThrow()
|
|
236
|
+
val inst = getInstanceOrThrow()
|
|
237
|
+
inst.applySecureEditor(inst.encryptedPreferences.edit().remove(key))
|
|
238
|
+
inst.applySecureEditor(inst.biometricPreferences.edit().remove(key))
|
|
171
239
|
}
|
|
172
240
|
|
|
173
241
|
@JvmStatic
|
|
174
242
|
fun deleteSecureBatch(keys: Array<String>) {
|
|
175
|
-
val
|
|
243
|
+
val inst = getInstanceOrThrow()
|
|
244
|
+
val secureEditor = inst.encryptedPreferences.edit()
|
|
245
|
+
val biometricEditor = inst.biometricPreferences.edit()
|
|
176
246
|
for (key in keys) {
|
|
177
|
-
|
|
247
|
+
secureEditor.remove(key)
|
|
248
|
+
biometricEditor.remove(key)
|
|
178
249
|
}
|
|
179
|
-
|
|
250
|
+
inst.applySecureEditor(secureEditor)
|
|
251
|
+
inst.applySecureEditor(biometricEditor)
|
|
180
252
|
}
|
|
181
253
|
|
|
182
254
|
@JvmStatic
|
|
183
|
-
fun
|
|
184
|
-
getInstanceOrThrow()
|
|
255
|
+
fun hasSecure(key: String): Boolean {
|
|
256
|
+
val inst = getInstanceOrThrow()
|
|
257
|
+
return inst.encryptedPreferences.contains(key) || inst.biometricPreferences.contains(key)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@JvmStatic
|
|
261
|
+
fun getAllKeysSecure(): Array<String> {
|
|
262
|
+
val inst = getInstanceOrThrow()
|
|
263
|
+
val keys = linkedSetOf<String>()
|
|
264
|
+
keys.addAll(inst.encryptedPreferences.all.keys)
|
|
265
|
+
keys.addAll(inst.biometricPreferences.all.keys)
|
|
266
|
+
return keys.toTypedArray()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@JvmStatic
|
|
270
|
+
fun sizeSecure(): Int {
|
|
271
|
+
return getAllKeysSecure().size
|
|
185
272
|
}
|
|
186
273
|
|
|
187
274
|
@JvmStatic
|
|
188
275
|
fun clearSecure() {
|
|
189
|
-
getInstanceOrThrow()
|
|
276
|
+
val inst = getInstanceOrThrow()
|
|
277
|
+
inst.applySecureEditor(inst.encryptedPreferences.edit().clear())
|
|
278
|
+
inst.applySecureEditor(inst.biometricPreferences.edit().clear())
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// --- Biometric (separate encrypted store, requires recent biometric auth on Android) ---
|
|
282
|
+
|
|
283
|
+
@JvmStatic
|
|
284
|
+
fun setSecureBiometric(key: String, value: String) {
|
|
285
|
+
val inst = getInstanceOrThrow()
|
|
286
|
+
val editor = inst.biometricPreferences.edit().putString(key, value)
|
|
287
|
+
inst.applySecureEditor(editor)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@JvmStatic
|
|
291
|
+
fun getSecureBiometric(key: String): String? {
|
|
292
|
+
return getInstanceOrThrow().getSecureSafe(getInstanceOrThrow().biometricPreferences, key)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@JvmStatic
|
|
296
|
+
fun deleteSecureBiometric(key: String) {
|
|
297
|
+
val inst = getInstanceOrThrow()
|
|
298
|
+
inst.applySecureEditor(inst.biometricPreferences.edit().remove(key))
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
@JvmStatic
|
|
302
|
+
fun hasSecureBiometric(key: String): Boolean {
|
|
303
|
+
return getInstanceOrThrow().biometricPreferences.contains(key)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
@JvmStatic
|
|
307
|
+
fun clearSecureBiometric() {
|
|
308
|
+
val inst = getInstanceOrThrow()
|
|
309
|
+
inst.applySecureEditor(inst.biometricPreferences.edit().clear())
|
|
190
310
|
}
|
|
191
311
|
}
|
|
192
312
|
}
|
|
@@ -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
|
}
|