react-native-security-suite 0.9.22 → 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 +235 -69
  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 -10
  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
 
@@ -43,18 +44,16 @@
43
44
 
44
45
  ## 🛠 Installation
45
46
 
46
- > **Note:** You must add `@react-native-async-storage/async-storage` in your app's `package.json` as well. React Native only autolinks native modules that are listed in your project's dependencies, so AsyncStorage must be a direct dependency of your app.
47
-
48
47
  ### Using Yarn
49
48
 
50
49
  ```bash
51
- yarn add react-native-security-suite @react-native-async-storage/async-storage
50
+ yarn add react-native-security-suite
52
51
  ```
53
52
 
54
53
  ### Using NPM
55
54
 
56
55
  ```bash
57
- npm install react-native-security-suite @react-native-async-storage/async-storage
56
+ npm install react-native-security-suite
58
57
  ```
59
58
 
60
59
  ### iOS Setup
@@ -63,6 +62,34 @@ npm install react-native-security-suite @react-native-async-storage/async-storag
63
62
  cd ios && pod install
64
63
  ```
65
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
+
66
93
  ## 📖 Usage Examples
67
94
 
68
95
  ### 1. Root/Jailbreak Detection
@@ -108,33 +135,22 @@ const SensitiveScreen = () => {
108
135
  };
109
136
  ```
110
137
 
111
- ### 3. Text Encryption & Decryption
138
+ ### 3. Obfuscation (local only)
112
139
 
113
- 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.
114
141
 
115
142
  ```javascript
116
- import { encrypt, decrypt } from 'react-native-security-suite';
117
-
118
- const handleEncryption = async () => {
119
- // Soft encryption (faster, less secure)
120
- const softEncrypted = await encrypt('Sensitive data', false);
121
- console.log('Soft encrypted:', softEncrypted);
122
-
123
- const softDecrypted = await decrypt(softEncrypted, false);
124
- console.log('Soft decrypted:', softDecrypted);
143
+ import { obfuscate, deobfuscate } from 'react-native-security-suite';
125
144
 
126
- // Hard encryption (slower, more secure)
127
- const hardEncrypted = await encrypt('Highly sensitive data', true);
128
- console.log('Hard encrypted:', hardEncrypted);
129
-
130
- const hardDecrypted = await decrypt(hardEncrypted, true);
131
- console.log('Hard decrypted:', hardDecrypted);
132
- };
145
+ const encoded = await obfuscate('local-cache-value', 'app-specific-secret');
146
+ const decoded = await deobfuscate(encoded, 'app-specific-secret');
133
147
  ```
134
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
+
135
151
  ### 4. Secure Storage
136
152
 
137
- 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`).
138
154
 
139
155
  ```javascript
140
156
  import { SecureStorage } from 'react-native-security-suite';
@@ -154,24 +170,39 @@ const handleSecureStorage = async () => {
154
170
  })
155
171
  );
156
172
 
157
- // Retrieve and decrypt data
173
+ // Retrieve data
158
174
  const token = await SecureStorage.getItem('userToken');
159
175
  const credentials = await SecureStorage.getItem('userCredentials');
176
+ const allKeys = await SecureStorage.getAllKeys();
160
177
 
161
178
  console.log('Retrieved token:', token);
162
179
  console.log('Retrieved credentials:', JSON.parse(credentials));
180
+ console.log('Stored keys:', allKeys);
163
181
 
164
182
  // Remove sensitive data
165
183
  await SecureStorage.removeItem('userToken');
184
+
185
+ // Clear all secure storage entries
186
+ await SecureStorage.clear();
166
187
  } catch (error) {
167
188
  console.error('Secure storage error:', error);
168
189
  }
169
190
  };
170
191
  ```
171
192
 
172
- ### 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:
196
+
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
173
204
 
174
- Implement secure key exchange for encrypted communications:
205
+ Implement secure key exchange with optional `CryptoOptions` (defaults: X25519 key agreement, AES-256-GCM, HMAC-SHA-512):
175
206
 
176
207
  ```javascript
177
208
  import {
@@ -181,33 +212,124 @@ import {
181
212
  decryptBySharedKey,
182
213
  } from 'react-native-security-suite';
183
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
+
184
223
  const handleKeyExchange = async () => {
185
- try {
186
- // Generate client public key
187
- const clientPublicKey = await getPublicKey();
188
- console.log('Client public key:', clientPublicKey);
224
+ const clientPublicKey = await getPublicKey();
225
+ const serverPublicKey = 'SERVER_PUBLIC_KEY_FROM_API';
189
226
 
190
- // Send to server and receive server's public key
191
- const serverPublicKey = 'SERVER_PUBLIC_KEY_FROM_API';
227
+ await getSharedKey(serverPublicKey, cryptoOptions);
192
228
 
193
- // Generate shared secret key
194
- const sharedKey = await getSharedKey(serverPublicKey);
195
- console.log('Shared key generated:', sharedKey);
229
+ const encryptedMessage = await encryptBySharedKey('Secret message', cryptoOptions);
230
+ const decryptedMessage = await decryptBySharedKey(encryptedMessage, cryptoOptions);
231
+ };
232
+ ```
196
233
 
197
- // Encrypt data with shared key
198
- const encryptedMessage = await encryptBySharedKey('Secret message');
199
- console.log('Encrypted message:', encryptedMessage);
234
+ ### 6. JWS Generation (RFC 7515)
200
235
 
201
- // Decrypt data with shared key
202
- const decryptedMessage = await decryptBySharedKey(encryptedMessage);
203
- console.log('Decrypted message:', decryptedMessage);
204
- } catch (error) {
205
- console.error('Key exchange error:', error);
206
- }
207
- };
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.
237
+
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
258
+ ```
259
+
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
+ });
208
269
  ```
209
270
 
210
- ### 6. SSL Certificate Pinning
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
211
333
 
212
334
  Secure your API communications with certificate pinning:
213
335
 
@@ -243,7 +365,7 @@ const secureApiCall = async () => {
243
365
  };
244
366
  ```
245
367
 
246
- ### 7. Network Monitoring & Debugging
368
+ ### 9. Network Monitoring & Debugging
247
369
 
248
370
  Monitor network requests in development:
249
371
 
@@ -274,37 +396,81 @@ const monitoredRequest = async () => {
274
396
 
275
397
  ### Security Detection
276
398
 
277
- - `deviceHasSecurityRisk()` - Detect rooted/jailbroken devices
399
+ - `deviceHasSecurityRisk()` Detect rooted/jailbroken devices
278
400
 
279
401
  ### Encryption & Storage
280
402
 
281
- - `encrypt(text, hardEncryption?, secretKey?)` - Encrypt text
282
- - `decrypt(encryptedText, hardEncryption?, secretKey?)` - Decrypt text
283
- - `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`
284
408
 
285
409
  ### Key Exchange
286
410
 
287
- - `getPublicKey()` - Generate public key
288
- - `getSharedKey(serverPublicKey)` - Generate shared key
289
- - `encryptBySharedKey(text)` - Encrypt with shared key
290
- - `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`.
291
453
 
292
454
  ### Network Security
293
455
 
294
- - `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.
295
459
 
296
460
  ### UI Components
297
461
 
298
- - `SecureView` - Screenshot-protected view component
462
+ - `SecureView` Screenshot-protected view component
299
463
 
300
464
  ## 🛡️ Security Best Practices
301
465
 
302
- 1. **Always validate certificates** - Use SSL pinning for production APIs
303
- 2. **Detect compromised devices** - Check for root/jailbreak before sensitive operations
304
- 3. **Use appropriate encryption levels** - Hard encryption for highly sensitive data
305
- 4. **Protect sensitive UI** - Wrap sensitive content in SecureView
306
- 5. **Monitor network traffic** - Use built-in logging for debugging
307
- 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
308
474
 
309
475
  ## 🐛 Troubleshooting
310
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
+ }