react-native-security-suite 0.9.21 → 1.0.0-rc.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.
Files changed (189) hide show
  1. package/README.md +233 -65
  2. package/android/build.gradle +11 -0
  3. package/android/gradle.properties +1 -1
  4. package/android/src/main/java/com/securitysuite/CryptoConfig.java +158 -0
  5. package/android/src/main/java/com/securitysuite/CryptoUtils.java +152 -0
  6. package/android/src/main/java/com/securitysuite/EcdhKeyStore.java +60 -0
  7. package/android/src/main/java/com/securitysuite/HeaderSanitizer.java +75 -0
  8. package/android/src/main/java/com/securitysuite/JWSGenerator.java +237 -32
  9. package/android/src/main/java/com/securitysuite/JwsFetchPayload.java +81 -0
  10. package/android/src/main/java/com/securitysuite/Obfuscation.java +57 -0
  11. package/android/src/main/java/com/securitysuite/SecureStorageNative.java +211 -0
  12. package/android/src/main/java/com/securitysuite/SecureView.java +2 -10
  13. package/android/src/main/java/com/securitysuite/SecureWindowHelper.java +30 -0
  14. package/android/src/main/java/com/securitysuite/SecuritySuiteModule.java +310 -102
  15. package/android/src/main/java/com/securitysuite/Sslpinning.java +219 -106
  16. package/android/src/main/java/com/securitysuite/security/AppIntegrityChecker.java +133 -0
  17. package/android/src/main/java/com/securitysuite/security/EmulatorDetector.java +145 -0
  18. package/android/src/main/java/com/securitysuite/security/RuntimeDetector.java +234 -0
  19. package/android/src/test/java/com/securitysuite/JWSGeneratorTest.java +153 -0
  20. package/android/src/test/java/com/securitysuite/SecureStorageNativeTest.java +37 -0
  21. package/ios/CryptoConfig.swift +124 -0
  22. package/ios/JWSGenerator.swift +288 -0
  23. package/ios/JWSGeneratorTests.swift +168 -0
  24. package/ios/KeychainHelper.swift +104 -0
  25. package/ios/Obfuscation.swift +42 -0
  26. package/ios/SecureStorageNative.swift +84 -0
  27. package/ios/Security/AppIntegrityChecker.swift +85 -0
  28. package/ios/Security/EmulatorDetector.swift +45 -0
  29. package/ios/Security/RuntimeDetector.swift +107 -0
  30. package/ios/SecuritySuite.mm +28 -4
  31. package/ios/SecuritySuite.swift +407 -131
  32. package/ios/SslPinning.swift +242 -263
  33. package/lib/commonjs/clipboard/index.js +3 -0
  34. package/lib/commonjs/clipboard/index.js.map +1 -0
  35. package/lib/commonjs/crypto/index.js +39 -0
  36. package/lib/commonjs/crypto/index.js.map +1 -0
  37. package/lib/commonjs/device/index.js +40 -0
  38. package/lib/commonjs/device/index.js.map +1 -0
  39. package/lib/commonjs/errors.js +62 -0
  40. package/lib/commonjs/errors.js.map +1 -0
  41. package/lib/commonjs/index.js +220 -151
  42. package/lib/commonjs/index.js.map +1 -1
  43. package/lib/commonjs/integrity/index.js +40 -0
  44. package/lib/commonjs/integrity/index.js.map +1 -0
  45. package/lib/commonjs/jws.js +141 -0
  46. package/lib/commonjs/jws.js.map +1 -0
  47. package/lib/commonjs/legacy/cryptoOptions.js +20 -0
  48. package/lib/commonjs/legacy/cryptoOptions.js.map +1 -0
  49. package/lib/commonjs/native/bridge.js +23 -0
  50. package/lib/commonjs/native/bridge.js.map +1 -0
  51. package/lib/commonjs/network/index.js +3 -0
  52. package/lib/commonjs/network/index.js.map +1 -0
  53. package/lib/commonjs/risk/score.js +36 -0
  54. package/lib/commonjs/risk/score.js.map +1 -0
  55. package/lib/commonjs/runtime/index.js +31 -0
  56. package/lib/commonjs/runtime/index.js.map +1 -0
  57. package/lib/commonjs/screen/index.js +13 -0
  58. package/lib/commonjs/screen/index.js.map +1 -0
  59. package/lib/commonjs/securitySuite/index.js +42 -0
  60. package/lib/commonjs/securitySuite/index.js.map +1 -0
  61. package/lib/commonjs/storage/index.js +3 -0
  62. package/lib/commonjs/storage/index.js.map +1 -0
  63. package/lib/commonjs/types/detection.js +2 -0
  64. package/lib/commonjs/types/detection.js.map +1 -0
  65. package/lib/module/clipboard/index.js +3 -0
  66. package/lib/module/clipboard/index.js.map +1 -0
  67. package/lib/module/crypto/index.js +35 -0
  68. package/lib/module/crypto/index.js.map +1 -0
  69. package/lib/module/device/index.js +36 -0
  70. package/lib/module/device/index.js.map +1 -0
  71. package/lib/module/errors.js +55 -0
  72. package/lib/module/errors.js.map +1 -0
  73. package/lib/module/index.js +147 -148
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/integrity/index.js +36 -0
  76. package/lib/module/integrity/index.js.map +1 -0
  77. package/lib/module/jws.js +127 -0
  78. package/lib/module/jws.js.map +1 -0
  79. package/lib/module/legacy/cryptoOptions.js +16 -0
  80. package/lib/module/legacy/cryptoOptions.js.map +1 -0
  81. package/lib/module/native/bridge.js +19 -0
  82. package/lib/module/native/bridge.js.map +1 -0
  83. package/lib/module/network/index.js +3 -0
  84. package/lib/module/network/index.js.map +1 -0
  85. package/lib/module/risk/score.js +32 -0
  86. package/lib/module/risk/score.js.map +1 -0
  87. package/lib/module/runtime/index.js +27 -0
  88. package/lib/module/runtime/index.js.map +1 -0
  89. package/lib/module/screen/index.js +5 -0
  90. package/lib/module/screen/index.js.map +1 -0
  91. package/lib/module/securitySuite/index.js +38 -0
  92. package/lib/module/securitySuite/index.js.map +1 -0
  93. package/lib/module/storage/index.js +3 -0
  94. package/lib/module/storage/index.js.map +1 -0
  95. package/lib/module/types/detection.js +2 -0
  96. package/lib/module/types/detection.js.map +1 -0
  97. package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts +215 -0
  98. package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts.map +1 -0
  99. package/lib/typescript/commonjs/src/SecureView.d.ts +1 -1
  100. package/lib/typescript/commonjs/src/SecureView.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/src/clipboard/index.d.ts +2 -0
  102. package/lib/typescript/commonjs/src/clipboard/index.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/src/crypto/index.d.ts +15 -0
  104. package/lib/typescript/commonjs/src/crypto/index.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/src/device/index.d.ts +11 -0
  106. package/lib/typescript/commonjs/src/device/index.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/src/errors.d.ts +17 -0
  108. package/lib/typescript/commonjs/src/errors.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/src/helpers.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/src/index.d.ts +77 -24
  111. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/src/integrity/index.d.ts +6 -0
  113. package/lib/typescript/commonjs/src/integrity/index.d.ts.map +1 -0
  114. package/lib/typescript/commonjs/src/jws.d.ts +44 -0
  115. package/lib/typescript/commonjs/src/jws.d.ts.map +1 -0
  116. package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts +35 -0
  117. package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts.map +1 -0
  118. package/lib/typescript/commonjs/src/native/bridge.d.ts +12 -0
  119. package/lib/typescript/commonjs/src/native/bridge.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/src/network/index.d.ts +2 -0
  121. package/lib/typescript/commonjs/src/network/index.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/src/risk/score.d.ts +12 -0
  123. package/lib/typescript/commonjs/src/risk/score.d.ts.map +1 -0
  124. package/lib/typescript/commonjs/src/runtime/index.d.ts +6 -0
  125. package/lib/typescript/commonjs/src/runtime/index.d.ts.map +1 -0
  126. package/lib/typescript/commonjs/src/screen/index.d.ts +3 -0
  127. package/lib/typescript/commonjs/src/screen/index.d.ts.map +1 -0
  128. package/lib/typescript/commonjs/src/securitySuite/index.d.ts +6 -0
  129. package/lib/typescript/commonjs/src/securitySuite/index.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/src/storage/index.d.ts +2 -0
  131. package/lib/typescript/commonjs/src/storage/index.d.ts.map +1 -0
  132. package/lib/typescript/commonjs/src/types/detection.d.ts +41 -0
  133. package/lib/typescript/commonjs/src/types/detection.d.ts.map +1 -0
  134. package/lib/typescript/module/docs/api-v1-proposal.d.ts +215 -0
  135. package/lib/typescript/module/docs/api-v1-proposal.d.ts.map +1 -0
  136. package/lib/typescript/module/src/SecureView.d.ts +1 -1
  137. package/lib/typescript/module/src/SecureView.d.ts.map +1 -1
  138. package/lib/typescript/module/src/clipboard/index.d.ts +2 -0
  139. package/lib/typescript/module/src/clipboard/index.d.ts.map +1 -0
  140. package/lib/typescript/module/src/crypto/index.d.ts +15 -0
  141. package/lib/typescript/module/src/crypto/index.d.ts.map +1 -0
  142. package/lib/typescript/module/src/device/index.d.ts +11 -0
  143. package/lib/typescript/module/src/device/index.d.ts.map +1 -0
  144. package/lib/typescript/module/src/errors.d.ts +17 -0
  145. package/lib/typescript/module/src/errors.d.ts.map +1 -0
  146. package/lib/typescript/module/src/helpers.d.ts.map +1 -1
  147. package/lib/typescript/module/src/index.d.ts +77 -24
  148. package/lib/typescript/module/src/index.d.ts.map +1 -1
  149. package/lib/typescript/module/src/integrity/index.d.ts +6 -0
  150. package/lib/typescript/module/src/integrity/index.d.ts.map +1 -0
  151. package/lib/typescript/module/src/jws.d.ts +44 -0
  152. package/lib/typescript/module/src/jws.d.ts.map +1 -0
  153. package/lib/typescript/module/src/legacy/cryptoOptions.d.ts +35 -0
  154. package/lib/typescript/module/src/legacy/cryptoOptions.d.ts.map +1 -0
  155. package/lib/typescript/module/src/native/bridge.d.ts +12 -0
  156. package/lib/typescript/module/src/native/bridge.d.ts.map +1 -0
  157. package/lib/typescript/module/src/network/index.d.ts +2 -0
  158. package/lib/typescript/module/src/network/index.d.ts.map +1 -0
  159. package/lib/typescript/module/src/risk/score.d.ts +12 -0
  160. package/lib/typescript/module/src/risk/score.d.ts.map +1 -0
  161. package/lib/typescript/module/src/runtime/index.d.ts +6 -0
  162. package/lib/typescript/module/src/runtime/index.d.ts.map +1 -0
  163. package/lib/typescript/module/src/screen/index.d.ts +3 -0
  164. package/lib/typescript/module/src/screen/index.d.ts.map +1 -0
  165. package/lib/typescript/module/src/securitySuite/index.d.ts +6 -0
  166. package/lib/typescript/module/src/securitySuite/index.d.ts.map +1 -0
  167. package/lib/typescript/module/src/storage/index.d.ts +2 -0
  168. package/lib/typescript/module/src/storage/index.d.ts.map +1 -0
  169. package/lib/typescript/module/src/types/detection.d.ts +41 -0
  170. package/lib/typescript/module/src/types/detection.d.ts.map +1 -0
  171. package/package.json +2 -4
  172. package/src/clipboard/index.ts +1 -0
  173. package/src/crypto/index.ts +49 -0
  174. package/src/device/index.ts +47 -0
  175. package/src/errors.ts +84 -0
  176. package/src/index.tsx +293 -195
  177. package/src/integrity/index.ts +46 -0
  178. package/src/jws.ts +213 -0
  179. package/src/legacy/cryptoOptions.ts +49 -0
  180. package/src/native/bridge.ts +37 -0
  181. package/src/network/index.ts +1 -0
  182. package/src/risk/score.ts +49 -0
  183. package/src/runtime/index.ts +43 -0
  184. package/src/screen/index.ts +2 -0
  185. package/src/securitySuite/index.ts +45 -0
  186. package/src/storage/index.ts +1 -0
  187. package/src/types/detection.ts +46 -0
  188. package/android/src/main/java/com/securitysuite/StorageEncryption.java +0 -52
  189. package/ios/StorageEncryption.swift +0 -89
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Downloads](https://img.shields.io/npm/dm/react-native-security-suite.svg)](https://www.npmjs.com/package/react-native-security-suite)
6
6
 
7
- **Comprehensive security solutions for React Native applications** - Protect your mobile apps with advanced security features including root/jailbreak detection, SSL certificate pinning, encryption, secure storage, screenshot protection, and network monitoring.
7
+ **Comprehensive security solutions for React Native applications** Protect your mobile apps with root/jailbreak detection, SSL certificate pinning, RFC 7515 JWS request signing, hardware-backed secure storage, X25519 key exchange, screenshot protection, and network monitoring.
8
8
 
9
9
  <div style="display: flex; flex-direction: row; justify-content: center; align-items: center; gap: 20px;">
10
10
  <img src="https://raw.githubusercontent.com/mohamadnavabi/react-native-security-suite/master/pulse.gif" alt="iOS Pulse Network Monitor" width="200" />
@@ -23,17 +23,18 @@
23
23
 
24
24
  ### Data Security & Encryption
25
25
 
26
- - **Text Encryption/Decryption**: Secure data encryption with multiple algorithms
27
- - **Secure Storage**: Encrypted local storage with AsyncStorage integration
28
- - **Diffie-Hellman Key Exchange**: Secure key generation and sharing
29
- - **Hard & Soft Encryption**: Multiple encryption levels for different security needs
26
+ - **Secure Storage**: Hardware-backed encrypted storage (Keychain on iOS, EncryptedSharedPreferences on Android)
27
+ - **Diffie-Hellman / X25519 Key Exchange**: Configurable key agreement with `CryptoOptions`
28
+ - **Shared-Key Encryption**: AES-GCM encrypt/decrypt using a derived shared secret
29
+ - **Obfuscation**: Local string obfuscation with an explicit secret (not for credentials at rest)
30
30
 
31
31
  ### Network Security & Monitoring
32
32
 
33
+ - **JWS Request Signing (RFC 7515)**: HMAC-signed compact JWS tokens for `fetch` requests (HS256/HS384/HS512)
34
+ - **SSL Certificate Pinning**: Pin SPKI SHA-256 hashes with domain allowlists
33
35
  - **Network Logger**: Built-in request/response logging
34
36
  - **Android Chucker Integration**: Advanced network debugging
35
37
  - **iOS Pulse Integration**: Network monitoring for iOS
36
- - **SSL Pinning with Custom Certificates**: Enhanced security for API calls
37
38
 
38
39
  ## 📱 Supported Platforms
39
40
 
@@ -61,6 +62,34 @@ npm install react-native-security-suite
61
62
  cd ios && pod install
62
63
  ```
63
64
 
65
+ ## 📁 Project Structure
66
+
67
+ The public API is exported from `src/index.tsx`. JWS validation and normalization live in a dedicated module so TypeScript and native code share the same contract.
68
+
69
+ ```
70
+ src/
71
+ ├── index.tsx # Public API (fetch, crypto, SecureStorage, SecureView)
72
+ ├── jws.ts # JWS types, validation, payload normalization
73
+ ├── SecureView.tsx # Screenshot-protected view component
74
+ └── helpers.ts # Internal utilities
75
+
76
+ android/src/main/java/com/securitysuite/
77
+ ├── SecureStorageNative.java # EncryptedSharedPreferences + Android Keystore
78
+ ├── JWSGenerator.java # RFC 7515 compact JWS (sign + verify)
79
+ ├── JwsFetchPayload.java # Default fetch signing payload builder
80
+ ├── SecuritySuiteModule.java
81
+ └── Sslpinning.java # fetch + SSL pinning + JWS header injection
82
+
83
+ ios/
84
+ ├── SecureStorageNative.swift # Keychain-backed secure storage
85
+ ├── KeychainHelper.swift # Keychain read/write/query helpers
86
+ ├── JWSGenerator.swift # RFC 7515 compact JWS (sign + verify)
87
+ ├── SecuritySuite.swift # Native module + fetch
88
+ └── SslPinning.swift
89
+ ```
90
+
91
+ **JWS flow:** JavaScript validates options in `src/jws.ts` (secret, algorithm, headers, payload shape), normalizes the payload to the exact UTF-8 signing string, then calls native `generateJWS` on Android/iOS. For `fetch`, native code builds the signing payload when `jws.payload` is omitted, signs it, and attaches the compact token to the request header.
92
+
64
93
  ## 📖 Usage Examples
65
94
 
66
95
  ### 1. Root/Jailbreak Detection
@@ -106,33 +135,22 @@ const SensitiveScreen = () => {
106
135
  };
107
136
  ```
108
137
 
109
- ### 3. Text Encryption & Decryption
138
+ ### 3. Obfuscation (local only)
110
139
 
111
- Secure your data with multiple encryption methods:
140
+ Obfuscation requires an explicit secret and is intended for non-sensitive local encoding — not for credentials, tokens, or PII at rest. Use `SecureStorage` for persisted secrets.
112
141
 
113
142
  ```javascript
114
- import { encrypt, decrypt } from 'react-native-security-suite';
115
-
116
- const handleEncryption = async () => {
117
- // Soft encryption (faster, less secure)
118
- const softEncrypted = await encrypt('Sensitive data', false);
119
- console.log('Soft encrypted:', softEncrypted);
120
-
121
- const softDecrypted = await decrypt(softEncrypted, false);
122
- console.log('Soft decrypted:', softDecrypted);
123
-
124
- // Hard encryption (slower, more secure)
125
- const hardEncrypted = await encrypt('Highly sensitive data', true);
126
- console.log('Hard encrypted:', hardEncrypted);
143
+ import { obfuscate, deobfuscate } from 'react-native-security-suite';
127
144
 
128
- const hardDecrypted = await decrypt(hardEncrypted, true);
129
- console.log('Hard decrypted:', hardDecrypted);
130
- };
145
+ const encoded = await obfuscate('local-cache-value', 'app-specific-secret');
146
+ const decoded = await deobfuscate(encoded, 'app-specific-secret');
131
147
  ```
132
148
 
149
+ > `encrypt()` / `decrypt()` remain available but are deprecated. They now require an explicit `secretKey` and should be replaced with `obfuscate()` / `deobfuscate()` or `SecureStorage`.
150
+
133
151
  ### 4. Secure Storage
134
152
 
135
- Store sensitive data securely with automatic encryption:
153
+ Store sensitive data in hardware-backed encrypted storage. On **iOS**, values are stored in the Keychain (`kSecClassGenericPassword`). On **Android**, values are encrypted with **EncryptedSharedPreferences** backed by Android Keystore (`AES256_GCM`).
136
154
 
137
155
  ```javascript
138
156
  import { SecureStorage } from 'react-native-security-suite';
@@ -152,24 +170,39 @@ const handleSecureStorage = async () => {
152
170
  })
153
171
  );
154
172
 
155
- // Retrieve and decrypt data
173
+ // Retrieve data
156
174
  const token = await SecureStorage.getItem('userToken');
157
175
  const credentials = await SecureStorage.getItem('userCredentials');
176
+ const allKeys = await SecureStorage.getAllKeys();
158
177
 
159
178
  console.log('Retrieved token:', token);
160
179
  console.log('Retrieved credentials:', JSON.parse(credentials));
180
+ console.log('Stored keys:', allKeys);
161
181
 
162
182
  // Remove sensitive data
163
183
  await SecureStorage.removeItem('userToken');
184
+
185
+ // Clear all secure storage entries
186
+ await SecureStorage.clear();
164
187
  } catch (error) {
165
188
  console.error('Secure storage error:', error);
166
189
  }
167
190
  };
168
191
  ```
169
192
 
170
- ### 5. Diffie-Hellman Key Exchange
193
+ #### Security Guarantee
194
+
195
+ `SecureStorage` no longer uses AsyncStorage or any JavaScript-side persistence. All keys and values are:
171
196
 
172
- Implement secure key exchange for encrypted communications:
197
+ - **Encrypted at rest** AES-GCM (Android) / Keychain-protected blobs (iOS)
198
+ - **Hardware-backed where available** — Android Keystore master key; iOS Keychain with `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`
199
+ - **System-managed keys** — No hardcoded salts or encryption keys in app code; the OS generates and protects master keys
200
+
201
+ If a native read/write fails (for example, KeyStore initialization errors on Android), the Promise rejects with a clear `Secure storage operation failed` error.
202
+
203
+ ### 5. Diffie-Hellman / X25519 Key Exchange
204
+
205
+ Implement secure key exchange with optional `CryptoOptions` (defaults: X25519 key agreement, AES-256-GCM, HMAC-SHA-512):
173
206
 
174
207
  ```javascript
175
208
  import {
@@ -179,33 +212,124 @@ import {
179
212
  decryptBySharedKey,
180
213
  } from 'react-native-security-suite';
181
214
 
215
+ const cryptoOptions = {
216
+ keyAgreementAlgorithm: 'X25519',
217
+ keyType: 'OKP',
218
+ encryptionKeyAlgorithm: 'AES-256',
219
+ hmacAlgorithm: 'HMAC-SHA-512',
220
+ cipher: 'AES-GCM',
221
+ };
222
+
182
223
  const handleKeyExchange = async () => {
183
- try {
184
- // Generate client public key
185
- const clientPublicKey = await getPublicKey();
186
- console.log('Client public key:', clientPublicKey);
224
+ const clientPublicKey = await getPublicKey();
225
+ const serverPublicKey = 'SERVER_PUBLIC_KEY_FROM_API';
187
226
 
188
- // Send to server and receive server's public key
189
- const serverPublicKey = 'SERVER_PUBLIC_KEY_FROM_API';
227
+ await getSharedKey(serverPublicKey, cryptoOptions);
228
+
229
+ const encryptedMessage = await encryptBySharedKey('Secret message', cryptoOptions);
230
+ const decryptedMessage = await decryptBySharedKey(encryptedMessage, cryptoOptions);
231
+ };
232
+ ```
190
233
 
191
- // Generate shared secret key
192
- const sharedKey = await getSharedKey(serverPublicKey);
193
- console.log('Shared key generated:', sharedKey);
234
+ ### 6. JWS Generation (RFC 7515)
194
235
 
195
- // Encrypt data with shared key
196
- const encryptedMessage = await encryptBySharedKey('Secret message');
197
- console.log('Encrypted message:', encryptedMessage);
236
+ Generate [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515) compact JWS tokens. Supported algorithms: **HS256**, **HS384**, **HS512**. An explicit `secret` is always required.
198
237
 
199
- // Decrypt data with shared key
200
- const decryptedMessage = await decryptBySharedKey(encryptedMessage);
201
- console.log('Decrypted message:', decryptedMessage);
202
- } catch (error) {
203
- console.error('Key exchange error:', error);
204
- }
205
- };
238
+ **How it works**
239
+
240
+ 1. TypeScript (`src/jws.ts`) validates `secret`, `algorithm`, and custom headers.
241
+ 2. The payload is normalized to the exact UTF-8 string used for signing (objects/arrays are `JSON.stringify`'d; `undefined`/`null`/`''` become an empty payload).
242
+ 3. Native code builds sorted protected headers (always including `alg`), base64url-encodes header and payload, signs `base64url(header).base64url(payload)` with HMAC, and returns the compact token.
243
+
244
+ **Empty payload** (`undefined`, `null`, or `''`) produces three segments with an empty middle segment:
245
+
246
+ ```javascript
247
+ import { generateJWS } from 'react-native-security-suite';
248
+
249
+ const jws = await generateJWS({
250
+ algorithm: 'HS256',
251
+ secret: 'my-temporary-secret',
252
+ headers: { kid: 'key-1' },
253
+ });
254
+
255
+ // compact form: <protectedHeader>.<payload>.<signature>
256
+ // jws.split('.').length === 3
257
+ // jws.split('.')[1] === '' // empty payload segment
206
258
  ```
207
259
 
208
- ### 6. SSL Certificate Pinning
260
+ **JSON payload:**
261
+
262
+ ```javascript
263
+ const jws = await generateJWS({
264
+ algorithm: 'HS512',
265
+ secret: 'my-temporary-secret',
266
+ payload: { amount: 1000, currency: 'USD' },
267
+ headers: { kid: 'key-1', request_id: 'req-123', typ: 'JWS' },
268
+ });
269
+ ```
270
+
271
+ **Header rules:** keys must match `^[a-zA-Z][a-zA-Z0-9_-]*$`; values must be JSON primitives (`string`, `number`, `boolean`, `null`). String values must be printable ASCII (`0x20`–`0x7E`). If both `algorithm` and `headers.alg` are set, they must match.
272
+
273
+ > **Security note:** HS* JWS on mobile uses a client-side shared secret. It helps with request integrity when combined with TLS, but it is not proof against a fully compromised client.
274
+
275
+ ### 7. Fetch Request Signing with JWS
276
+
277
+ Pass a `jws` object on `fetch` options. The signed token is sent as an HTTP header (default: `X-Request-Signature`).
278
+
279
+ ```javascript
280
+ import { fetch } from 'react-native-security-suite';
281
+
282
+ await fetch('https://api.example.com/payments', {
283
+ method: 'POST',
284
+ headers: { 'Content-Type': 'application/json' },
285
+ body: { amount: 1000 },
286
+ jws: {
287
+ algorithm: 'HS256',
288
+ secret: 'temporary-session-secret',
289
+ headers: {
290
+ kid: 'key-1',
291
+ request_id: 'req-123',
292
+ timestamp: Date.now(),
293
+ nonce: 'unique-nonce-per-request',
294
+ },
295
+ headerName: 'X-Request-Signature',
296
+ detached: false,
297
+ },
298
+ });
299
+ ```
300
+
301
+ **Default signing payload** (when `jws.payload` is omitted): native code builds a sorted JSON object from:
302
+
303
+ | Field | Source |
304
+ |-------|--------|
305
+ | `method` | HTTP method (uppercased) |
306
+ | `path` | URL path (`/` if empty) |
307
+ | `query` | Query string (if present) |
308
+ | `bodyHash` | Base64url SHA-256 of request body (if body present) |
309
+ | `timestamp`, `nonce`, `request_id` | Copied from `jws.headers` when provided |
310
+
311
+ **Explicit fetch payload:** when you set `jws.payload`, it must already be a string (use `JSON.stringify` for objects). For fetch, object payloads are not auto-serialized — only `generateJWS` accepts object payloads directly.
312
+
313
+ ```javascript
314
+ jws: {
315
+ secret: 'temporary-session-secret',
316
+ payload: JSON.stringify({ amount: 1000, currency: 'USD' }),
317
+ }
318
+ ```
319
+
320
+ **Detached fetch signing** — set `jws.detached: true` to send `header..signature` while the raw payload remains in the request body:
321
+
322
+ ```javascript
323
+ jws: {
324
+ secret: 'temporary-session-secret',
325
+ detached: true,
326
+ headers: { kid: 'key-1', request_id: 'req-123' },
327
+ }
328
+ ```
329
+
330
+ **Migration from legacy options:** `options.keyId`, `options.requestId`, and top-level `options.secret` are deprecated. Use `options.jws` with `secret`, `headers.kid`, and `headers.request_id` instead. Legacy signing always used detached HS256 and the `X-JWS-Signature` header.
331
+
332
+ ### 8. SSL Certificate Pinning
209
333
 
210
334
  Secure your API communications with certificate pinning:
211
335
 
@@ -241,7 +365,7 @@ const secureApiCall = async () => {
241
365
  };
242
366
  ```
243
367
 
244
- ### 7. Network Monitoring & Debugging
368
+ ### 9. Network Monitoring & Debugging
245
369
 
246
370
  Monitor network requests in development:
247
371
 
@@ -272,37 +396,81 @@ const monitoredRequest = async () => {
272
396
 
273
397
  ### Security Detection
274
398
 
275
- - `deviceHasSecurityRisk()` - Detect rooted/jailbroken devices
399
+ - `deviceHasSecurityRisk()` Detect rooted/jailbroken devices
276
400
 
277
401
  ### Encryption & Storage
278
402
 
279
- - `encrypt(text, hardEncryption?, secretKey?)` - Encrypt text
280
- - `decrypt(encryptedText, hardEncryption?, secretKey?)` - Decrypt text
281
- - `SecureStorage` - Encrypted storage methods
403
+ - `obfuscate(input, secret)` Local obfuscation with explicit secret
404
+ - `deobfuscate(input, secret)` Reverse `obfuscate`
405
+ - `SecureStorage` — Hardware-backed encrypted storage (`setItem`, `getItem`, `removeItem`, `getAllKeys`, `clear`, `multiGet`, `multiSet`, `multiRemove`)
406
+ - `encrypt(text, hardEncryption?, secretKey?)` — **deprecated**; requires `secretKey`
407
+ - `decrypt(encryptedText, hardEncryption?, secretKey?)` — **deprecated**; requires `secretKey`
282
408
 
283
409
  ### Key Exchange
284
410
 
285
- - `getPublicKey()` - Generate public key
286
- - `getSharedKey(serverPublicKey)` - Generate shared key
287
- - `encryptBySharedKey(text)` - Encrypt with shared key
288
- - `decryptBySharedKey(encryptedText)` - Decrypt with shared key
411
+ - `getPublicKey()` Generate client public key (JWK)
412
+ - `getSharedKey(serverPublicKey, options?)` Derive shared secret; accepts `CryptoOptions`
413
+ - `encryptBySharedKey(text, options?)` — AES-GCM encrypt with derived key
414
+ - `decryptBySharedKey(encryptedText, options?)` — AES-GCM decrypt with derived key
415
+
416
+ #### `CryptoOptions`
417
+
418
+ | Option | Default | Description |
419
+ |--------|---------|-------------|
420
+ | `keyAgreementAlgorithm` | `'X25519'` | Key agreement (e.g. `X25519`, `ECDH`) |
421
+ | `keyType` | `'OKP'` | JWK key type (`OKP`, `EC`) |
422
+ | `encryptionKeyAlgorithm` | `'AES-256'` | Symmetric key algorithm |
423
+ | `hmacAlgorithm` | `'HMAC-SHA-512'` | HMAC for signing and key derivation |
424
+ | `cipher` | `'AES-GCM'` | AEAD cipher mode |
425
+ | `tagLength` | `128` | GCM authentication tag length (bits) |
426
+ | `ivLength` | `12` | GCM IV length (bytes) |
427
+
428
+ ### JWS
429
+
430
+ - `generateJWS(options: GenerateJWSOptions)` — Generate compact or detached JWS
431
+
432
+ #### `GenerateJWSOptions`
433
+
434
+ | Field | Required | Description |
435
+ |-------|----------|-------------|
436
+ | `secret` | yes | Non-empty HMAC secret |
437
+ | `algorithm` | no | `HS256` (default), `HS384`, or `HS512` |
438
+ | `payload` | no | String, object, array, number, boolean, `null`, or `undefined` |
439
+ | `headers` | no | Custom protected headers (`kid`, `request_id`, etc.) |
440
+
441
+ #### `JwsFetchOptions` (on `fetch` options)
442
+
443
+ | Field | Required | Description |
444
+ |-------|----------|-------------|
445
+ | `secret` | yes | Non-empty HMAC secret |
446
+ | `algorithm` | no | `HS256` (default), `HS384`, or `HS512` |
447
+ | `headers` | no | Protected headers; `timestamp`/`nonce`/`request_id` also feed the default payload |
448
+ | `payload` | no | Explicit signing string; omit to use the default fetch payload |
449
+ | `detached` | no | Detached compact form (default: `false`) |
450
+ | `headerName` | no | Request header name (default: `X-Request-Signature`) |
451
+
452
+ Exported types: `JwsAlgorithm`, `JwsPayload`, `JwsHeaders`, `JwsHeaderValue`, `GenerateJWSOptions`, `JwsFetchOptions`.
289
453
 
290
454
  ### Network Security
291
455
 
292
- - `fetch(url, options, loggerEnabled?)` - Secure fetch with SSL pinning
456
+ - `fetch(url, options, loggerEnabled?)` Secure fetch with SSL pinning and optional JWS signing
457
+
458
+ `fetch` `options` also accepts `certificates` + `validDomains` for SSL pinning, and deprecated top-level `keyId`, `requestId`, `secret` for legacy JWS.
293
459
 
294
460
  ### UI Components
295
461
 
296
- - `SecureView` - Screenshot-protected view component
462
+ - `SecureView` Screenshot-protected view component
297
463
 
298
464
  ## 🛡️ Security Best Practices
299
465
 
300
- 1. **Always validate certificates** - Use SSL pinning for production APIs
301
- 2. **Detect compromised devices** - Check for root/jailbreak before sensitive operations
302
- 3. **Use appropriate encryption levels** - Hard encryption for highly sensitive data
303
- 4. **Protect sensitive UI** - Wrap sensitive content in SecureView
304
- 5. **Monitor network traffic** - Use built-in logging for debugging
305
- 6. **Secure key management** - Implement proper key exchange protocols
466
+ 1. **Always validate certificates** Use SSL pinning for production APIs
467
+ 2. **Detect compromised devices** Check for root/jailbreak before sensitive operations
468
+ 3. **Store secrets in SecureStorage** — Use hardware-backed storage for tokens and credentials
469
+ 4. **Protect sensitive UI** Wrap sensitive content in `SecureView`
470
+ 5. **Sign requests with JWS** Include `timestamp`, `nonce`, and `request_id` in `jws.headers` for replay protection; never embed long-lived secrets in the app binary
471
+ 6. **Use detached JWS for body signing** When the request body is the signed payload, set `jws.detached: true` so the body is not duplicated inside the token
472
+ 7. **Monitor network traffic** — Use built-in logging for debugging only; disable in production
473
+ 8. **Rotate session secrets** — Treat `jws.secret` as a short-lived session or request-scoped value from your backend
306
474
 
307
475
  ## 🐛 Troubleshooting
308
476
 
@@ -76,6 +76,12 @@ android {
76
76
  }
77
77
  }
78
78
 
79
+ testOptions {
80
+ unitTests.all {
81
+ it.useJUnit()
82
+ }
83
+ }
84
+
79
85
  lintOptions {
80
86
  disable "GradleCompatible"
81
87
  }
@@ -90,6 +96,7 @@ android {
90
96
  }
91
97
 
92
98
  buildFeatures {
99
+ buildConfig true
93
100
  dataBinding true
94
101
  }
95
102
  }
@@ -111,5 +118,9 @@ dependencies {
111
118
  implementation "com.squareup.okhttp3:okhttp:4.12.0"
112
119
  implementation "com.scottyab:rootbeer-lib:0.1.0"
113
120
  implementation "com.github.chuckerteam.chucker:library:4.1.0"
121
+ implementation "androidx.security:security-crypto:1.1.0-alpha06"
122
+
123
+ testImplementation "junit:junit:4.13.2"
124
+ testImplementation "com.facebook.react:react-native:+"
114
125
  }
115
126
 
@@ -1,5 +1,5 @@
1
1
  SecuritySuite_kotlinVersion=1.7.0
2
- SecuritySuite_minSdkVersion=21
2
+ SecuritySuite_minSdkVersion=23
3
3
  SecuritySuite_targetSdkVersion=31
4
4
  SecuritySuite_compileSdkVersion=31
5
5
  SecuritySuite_ndkversion=21.4.7075529
@@ -0,0 +1,158 @@
1
+ package com.securitysuite;
2
+
3
+ import com.facebook.react.bridge.ReadableMap;
4
+
5
+ /**
6
+ * Configurable cryptographic parameters with secure defaults and whitelist validation.
7
+ */
8
+ public final class CryptoConfig {
9
+ public static final String DEFAULT_KEY_AGREEMENT = "ECDH";
10
+ public static final String DEFAULT_KEY_FACTORY = "EC";
11
+ public static final String DEFAULT_ENCRYPTION_KEY_ALGORITHM = "AES";
12
+ public static final String DEFAULT_HMAC_KEY_ALGORITHM = "HmacSHA256";
13
+ public static final String DEFAULT_CIPHER_TRANSFORMATION = "AES/GCM/NoPadding";
14
+ public static final int DEFAULT_GCM_TAG_LENGTH = 128;
15
+ public static final int DEFAULT_GCM_IV_LENGTH = 12;
16
+
17
+ public final String keyAgreementAlgorithm;
18
+ public final String keyFactoryAlgorithm;
19
+ public final String encryptionKeyAlgorithm;
20
+ public final String hmacKeyAlgorithm;
21
+ public final String cipherTransformation;
22
+ public final int gcmTagLength;
23
+ public final int gcmIvLength;
24
+
25
+ private CryptoConfig(
26
+ String keyAgreementAlgorithm,
27
+ String keyFactoryAlgorithm,
28
+ String encryptionKeyAlgorithm,
29
+ String hmacKeyAlgorithm,
30
+ String cipherTransformation,
31
+ int gcmTagLength,
32
+ int gcmIvLength
33
+ ) {
34
+ this.keyAgreementAlgorithm = keyAgreementAlgorithm;
35
+ this.keyFactoryAlgorithm = keyFactoryAlgorithm;
36
+ this.encryptionKeyAlgorithm = encryptionKeyAlgorithm;
37
+ this.hmacKeyAlgorithm = hmacKeyAlgorithm;
38
+ this.cipherTransformation = cipherTransformation;
39
+ this.gcmTagLength = gcmTagLength;
40
+ this.gcmIvLength = gcmIvLength;
41
+ }
42
+
43
+ public static CryptoConfig defaults() {
44
+ return new CryptoConfig(
45
+ DEFAULT_KEY_AGREEMENT,
46
+ DEFAULT_KEY_FACTORY,
47
+ DEFAULT_ENCRYPTION_KEY_ALGORITHM,
48
+ DEFAULT_HMAC_KEY_ALGORITHM,
49
+ DEFAULT_CIPHER_TRANSFORMATION,
50
+ DEFAULT_GCM_TAG_LENGTH,
51
+ DEFAULT_GCM_IV_LENGTH
52
+ );
53
+ }
54
+
55
+ public static CryptoConfig fromReadableMap(ReadableMap options) {
56
+ if (options == null) {
57
+ return defaults();
58
+ }
59
+
60
+ return new CryptoConfig(
61
+ validateKeyAgreement(readString(options, "keyAgreementAlgorithm", DEFAULT_KEY_AGREEMENT)),
62
+ validateKeyFactory(readString(options, "keyFactoryAlgorithm", DEFAULT_KEY_FACTORY)),
63
+ validateEncryptionKeyAlgorithm(
64
+ readString(options, "encryptionKeyAlgorithm", DEFAULT_ENCRYPTION_KEY_ALGORITHM)
65
+ ),
66
+ validateHmacKeyAlgorithm(readString(options, "hmacKeyAlgorithm", DEFAULT_HMAC_KEY_ALGORITHM)),
67
+ validateCipherTransformation(
68
+ readString(options, "cipherTransformation", DEFAULT_CIPHER_TRANSFORMATION)
69
+ ),
70
+ options.hasKey("gcmTagLength") ? options.getInt("gcmTagLength") : DEFAULT_GCM_TAG_LENGTH,
71
+ options.hasKey("gcmIvLength") ? options.getInt("gcmIvLength") : DEFAULT_GCM_IV_LENGTH
72
+ );
73
+ }
74
+
75
+ public CryptoConfig merge(ReadableMap options) {
76
+ return options == null ? this : fromReadableMap(mergeMaps(this, options));
77
+ }
78
+
79
+ private static ReadableMap mergeMaps(CryptoConfig base, ReadableMap overrides) {
80
+ com.facebook.react.bridge.WritableMap map = com.facebook.react.bridge.Arguments.createMap();
81
+ map.putString("keyAgreementAlgorithm", base.keyAgreementAlgorithm);
82
+ map.putString("keyFactoryAlgorithm", base.keyFactoryAlgorithm);
83
+ map.putString("encryptionKeyAlgorithm", base.encryptionKeyAlgorithm);
84
+ map.putString("hmacKeyAlgorithm", base.hmacKeyAlgorithm);
85
+ map.putString("cipherTransformation", base.cipherTransformation);
86
+ map.putInt("gcmTagLength", base.gcmTagLength);
87
+ map.putInt("gcmIvLength", base.gcmIvLength);
88
+
89
+ if (overrides.hasKey("keyAgreementAlgorithm")) {
90
+ map.putString("keyAgreementAlgorithm", overrides.getString("keyAgreementAlgorithm"));
91
+ }
92
+ if (overrides.hasKey("keyFactoryAlgorithm")) {
93
+ map.putString("keyFactoryAlgorithm", overrides.getString("keyFactoryAlgorithm"));
94
+ }
95
+ if (overrides.hasKey("encryptionKeyAlgorithm")) {
96
+ map.putString("encryptionKeyAlgorithm", overrides.getString("encryptionKeyAlgorithm"));
97
+ }
98
+ if (overrides.hasKey("hmacKeyAlgorithm")) {
99
+ map.putString("hmacKeyAlgorithm", overrides.getString("hmacKeyAlgorithm"));
100
+ }
101
+ if (overrides.hasKey("cipherTransformation")) {
102
+ map.putString("cipherTransformation", overrides.getString("cipherTransformation"));
103
+ }
104
+ if (overrides.hasKey("gcmTagLength")) {
105
+ map.putInt("gcmTagLength", overrides.getInt("gcmTagLength"));
106
+ }
107
+ if (overrides.hasKey("gcmIvLength")) {
108
+ map.putInt("gcmIvLength", overrides.getInt("gcmIvLength"));
109
+ }
110
+ return map;
111
+ }
112
+
113
+ private static String readString(ReadableMap map, String key, String defaultValue) {
114
+ if (map.hasKey(key) && map.getString(key) != null) {
115
+ return map.getString(key).trim();
116
+ }
117
+ return defaultValue;
118
+ }
119
+
120
+ private static String validateKeyAgreement(String algorithm) {
121
+ if ("ECDH".equals(algorithm)) {
122
+ return algorithm;
123
+ }
124
+ throw new IllegalArgumentException("Unsupported keyAgreementAlgorithm: " + algorithm);
125
+ }
126
+
127
+ private static String validateKeyFactory(String algorithm) {
128
+ if ("EC".equals(algorithm)) {
129
+ return algorithm;
130
+ }
131
+ throw new IllegalArgumentException("Unsupported keyFactoryAlgorithm: " + algorithm);
132
+ }
133
+
134
+ private static String validateEncryptionKeyAlgorithm(String algorithm) {
135
+ if ("AES".equals(algorithm)) {
136
+ return algorithm;
137
+ }
138
+ throw new IllegalArgumentException("Unsupported encryptionKeyAlgorithm: " + algorithm);
139
+ }
140
+
141
+ private static String validateHmacKeyAlgorithm(String algorithm) {
142
+ switch (algorithm) {
143
+ case "HmacSHA256":
144
+ case "HmacSHA384":
145
+ case "HmacSHA512":
146
+ return algorithm;
147
+ default:
148
+ throw new IllegalArgumentException("Unsupported hmacKeyAlgorithm: " + algorithm);
149
+ }
150
+ }
151
+
152
+ private static String validateCipherTransformation(String transformation) {
153
+ if ("AES/GCM/NoPadding".equals(transformation)) {
154
+ return transformation;
155
+ }
156
+ throw new IllegalArgumentException("Unsupported cipherTransformation: " + transformation);
157
+ }
158
+ }