react-native-security-suite 0.9.22 → 1.0.0-rc.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 +291 -69
- package/android/build.gradle +11 -0
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/securitysuite/CryptoConfig.java +106 -0
- package/android/src/main/java/com/securitysuite/CryptoUtils.java +155 -0
- package/android/src/main/java/com/securitysuite/EcdhKeyStore.java +60 -0
- package/android/src/main/java/com/securitysuite/HeaderSanitizer.java +75 -0
- package/android/src/main/java/com/securitysuite/JWSGenerator.java +237 -32
- package/android/src/main/java/com/securitysuite/JwsFetchPayload.java +81 -0
- package/android/src/main/java/com/securitysuite/Obfuscation.java +57 -0
- package/android/src/main/java/com/securitysuite/SecureStorageNative.java +211 -0
- package/android/src/main/java/com/securitysuite/SecureView.java +2 -10
- package/android/src/main/java/com/securitysuite/SecureWindowHelper.java +30 -0
- package/android/src/main/java/com/securitysuite/SecuritySuiteModule.java +317 -102
- package/android/src/main/java/com/securitysuite/Sslpinning.java +219 -106
- package/android/src/main/java/com/securitysuite/security/AppIntegrityChecker.java +133 -0
- package/android/src/main/java/com/securitysuite/security/EmulatorDetector.java +145 -0
- package/android/src/main/java/com/securitysuite/security/RuntimeDetector.java +234 -0
- package/android/src/test/java/com/securitysuite/JWSGeneratorTest.java +153 -0
- package/android/src/test/java/com/securitysuite/SecureStorageNativeTest.java +37 -0
- package/ios/CryptoConfig.swift +73 -0
- package/ios/JWSGenerator.swift +288 -0
- package/ios/JWSGeneratorTests.swift +168 -0
- package/ios/KeychainHelper.swift +104 -0
- package/ios/Obfuscation.swift +42 -0
- package/ios/SecureStorageNative.swift +84 -0
- package/ios/Security/AppIntegrityChecker.swift +85 -0
- package/ios/Security/EmulatorDetector.swift +45 -0
- package/ios/Security/RuntimeDetector.swift +107 -0
- package/ios/SecuritySuite.mm +28 -4
- package/ios/SecuritySuite.swift +427 -134
- package/ios/SslPinning.swift +242 -263
- package/lib/commonjs/clipboard/index.js +3 -0
- package/lib/commonjs/clipboard/index.js.map +1 -0
- package/lib/commonjs/crypto/index.js +29 -0
- package/lib/commonjs/crypto/index.js.map +1 -0
- package/lib/commonjs/device/index.js +40 -0
- package/lib/commonjs/device/index.js.map +1 -0
- package/lib/commonjs/errors.js +62 -0
- package/lib/commonjs/errors.js.map +1 -0
- package/lib/commonjs/index.js +220 -151
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/integrity/index.js +40 -0
- package/lib/commonjs/integrity/index.js.map +1 -0
- package/lib/commonjs/jws.js +141 -0
- package/lib/commonjs/jws.js.map +1 -0
- package/lib/commonjs/legacy/cryptoOptions.js +29 -0
- package/lib/commonjs/legacy/cryptoOptions.js.map +1 -0
- package/lib/commonjs/native/bridge.js +23 -0
- package/lib/commonjs/native/bridge.js.map +1 -0
- package/lib/commonjs/network/index.js +3 -0
- package/lib/commonjs/network/index.js.map +1 -0
- package/lib/commonjs/risk/score.js +36 -0
- package/lib/commonjs/risk/score.js.map +1 -0
- package/lib/commonjs/runtime/index.js +31 -0
- package/lib/commonjs/runtime/index.js.map +1 -0
- package/lib/commonjs/screen/index.js +13 -0
- package/lib/commonjs/screen/index.js.map +1 -0
- package/lib/commonjs/securitySuite/index.js +42 -0
- package/lib/commonjs/securitySuite/index.js.map +1 -0
- package/lib/commonjs/storage/index.js +3 -0
- package/lib/commonjs/storage/index.js.map +1 -0
- package/lib/commonjs/types/detection.js +2 -0
- package/lib/commonjs/types/detection.js.map +1 -0
- package/lib/module/clipboard/index.js +3 -0
- package/lib/module/clipboard/index.js.map +1 -0
- package/lib/module/crypto/index.js +25 -0
- package/lib/module/crypto/index.js.map +1 -0
- package/lib/module/device/index.js +36 -0
- package/lib/module/device/index.js.map +1 -0
- package/lib/module/errors.js +55 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/index.js +147 -148
- package/lib/module/index.js.map +1 -1
- package/lib/module/integrity/index.js +36 -0
- package/lib/module/integrity/index.js.map +1 -0
- package/lib/module/jws.js +127 -0
- package/lib/module/jws.js.map +1 -0
- package/lib/module/legacy/cryptoOptions.js +25 -0
- package/lib/module/legacy/cryptoOptions.js.map +1 -0
- package/lib/module/native/bridge.js +19 -0
- package/lib/module/native/bridge.js.map +1 -0
- package/lib/module/network/index.js +3 -0
- package/lib/module/network/index.js.map +1 -0
- package/lib/module/risk/score.js +32 -0
- package/lib/module/risk/score.js.map +1 -0
- package/lib/module/runtime/index.js +27 -0
- package/lib/module/runtime/index.js.map +1 -0
- package/lib/module/screen/index.js +5 -0
- package/lib/module/screen/index.js.map +1 -0
- package/lib/module/securitySuite/index.js +38 -0
- package/lib/module/securitySuite/index.js.map +1 -0
- package/lib/module/storage/index.js +3 -0
- package/lib/module/storage/index.js.map +1 -0
- package/lib/module/types/detection.js +2 -0
- package/lib/module/types/detection.js.map +1 -0
- package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts +215 -0
- package/lib/typescript/commonjs/docs/api-v1-proposal.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/SecureView.d.ts +1 -1
- package/lib/typescript/commonjs/src/SecureView.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/clipboard/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/clipboard/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/crypto/index.d.ts +15 -0
- package/lib/typescript/commonjs/src/crypto/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/device/index.d.ts +11 -0
- package/lib/typescript/commonjs/src/device/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/errors.d.ts +17 -0
- package/lib/typescript/commonjs/src/errors.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/helpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +77 -24
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/integrity/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/integrity/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/jws.d.ts +44 -0
- package/lib/typescript/commonjs/src/jws.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts +35 -0
- package/lib/typescript/commonjs/src/legacy/cryptoOptions.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/native/bridge.d.ts +12 -0
- package/lib/typescript/commonjs/src/native/bridge.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/network/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/network/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/risk/score.d.ts +12 -0
- package/lib/typescript/commonjs/src/risk/score.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/runtime/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/runtime/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/screen/index.d.ts +3 -0
- package/lib/typescript/commonjs/src/screen/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/securitySuite/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/securitySuite/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/storage/index.d.ts +2 -0
- package/lib/typescript/commonjs/src/storage/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/types/detection.d.ts +41 -0
- package/lib/typescript/commonjs/src/types/detection.d.ts.map +1 -0
- package/lib/typescript/module/docs/api-v1-proposal.d.ts +215 -0
- package/lib/typescript/module/docs/api-v1-proposal.d.ts.map +1 -0
- package/lib/typescript/module/src/SecureView.d.ts +1 -1
- package/lib/typescript/module/src/SecureView.d.ts.map +1 -1
- package/lib/typescript/module/src/clipboard/index.d.ts +2 -0
- package/lib/typescript/module/src/clipboard/index.d.ts.map +1 -0
- package/lib/typescript/module/src/crypto/index.d.ts +15 -0
- package/lib/typescript/module/src/crypto/index.d.ts.map +1 -0
- package/lib/typescript/module/src/device/index.d.ts +11 -0
- package/lib/typescript/module/src/device/index.d.ts.map +1 -0
- package/lib/typescript/module/src/errors.d.ts +17 -0
- package/lib/typescript/module/src/errors.d.ts.map +1 -0
- package/lib/typescript/module/src/helpers.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +77 -24
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/integrity/index.d.ts +6 -0
- package/lib/typescript/module/src/integrity/index.d.ts.map +1 -0
- package/lib/typescript/module/src/jws.d.ts +44 -0
- package/lib/typescript/module/src/jws.d.ts.map +1 -0
- package/lib/typescript/module/src/legacy/cryptoOptions.d.ts +35 -0
- package/lib/typescript/module/src/legacy/cryptoOptions.d.ts.map +1 -0
- package/lib/typescript/module/src/native/bridge.d.ts +12 -0
- package/lib/typescript/module/src/native/bridge.d.ts.map +1 -0
- package/lib/typescript/module/src/network/index.d.ts +2 -0
- package/lib/typescript/module/src/network/index.d.ts.map +1 -0
- package/lib/typescript/module/src/risk/score.d.ts +12 -0
- package/lib/typescript/module/src/risk/score.d.ts.map +1 -0
- package/lib/typescript/module/src/runtime/index.d.ts +6 -0
- package/lib/typescript/module/src/runtime/index.d.ts.map +1 -0
- package/lib/typescript/module/src/screen/index.d.ts +3 -0
- package/lib/typescript/module/src/screen/index.d.ts.map +1 -0
- package/lib/typescript/module/src/securitySuite/index.d.ts +6 -0
- package/lib/typescript/module/src/securitySuite/index.d.ts.map +1 -0
- package/lib/typescript/module/src/storage/index.d.ts +2 -0
- package/lib/typescript/module/src/storage/index.d.ts.map +1 -0
- package/lib/typescript/module/src/types/detection.d.ts +41 -0
- package/lib/typescript/module/src/types/detection.d.ts.map +1 -0
- package/package.json +2 -10
- package/src/clipboard/index.ts +1 -0
- package/src/crypto/index.ts +40 -0
- package/src/device/index.ts +47 -0
- package/src/errors.ts +84 -0
- package/src/index.tsx +293 -195
- package/src/integrity/index.ts +46 -0
- package/src/jws.ts +213 -0
- package/src/legacy/cryptoOptions.ts +84 -0
- package/src/native/bridge.ts +37 -0
- package/src/network/index.ts +1 -0
- package/src/risk/score.ts +49 -0
- package/src/runtime/index.ts +43 -0
- package/src/screen/index.ts +2 -0
- package/src/securitySuite/index.ts +45 -0
- package/src/storage/index.ts +1 -0
- package/src/types/detection.ts +46 -0
- package/android/src/main/java/com/securitysuite/StorageEncryption.java +0 -52
- package/ios/StorageEncryption.swift +0 -89
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.npmjs.com/package/react-native-security-suite)
|
|
6
6
|
|
|
7
|
-
**Comprehensive security solutions for React Native applications**
|
|
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, configurable ECDH 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
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
26
|
+
- **Secure Storage**: Hardware-backed encrypted storage (Keychain on iOS, EncryptedSharedPreferences on Android)
|
|
27
|
+
- **Diffie-Hellman Key Exchange**: Application-defined algorithms and GCM parameters via `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
|
|
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
|
|
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.
|
|
138
|
+
### 3. Obfuscation (local only)
|
|
112
139
|
|
|
113
|
-
|
|
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 {
|
|
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
|
-
|
|
127
|
-
|
|
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
|
|
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,60 +170,216 @@ const handleSecureStorage = async () => {
|
|
|
154
170
|
})
|
|
155
171
|
);
|
|
156
172
|
|
|
157
|
-
// Retrieve
|
|
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
|
|
|
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
|
+
|
|
172
203
|
### 5. Diffie-Hellman Key Exchange
|
|
173
204
|
|
|
174
|
-
|
|
205
|
+
Derive a shared encryption key from the client and server public keys. **All cryptographic algorithms and GCM lengths must be supplied by your application** — the native layer does not apply defaults.
|
|
175
206
|
|
|
176
|
-
|
|
207
|
+
Use **`Crypto.establishSharedKey()`** so the derived key stays in native memory. Pass the same `cryptoOptions` to `encryptBySharedKey` / `decryptBySharedKey`.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
177
210
|
import {
|
|
211
|
+
Crypto,
|
|
178
212
|
getPublicKey,
|
|
179
|
-
getSharedKey,
|
|
180
213
|
encryptBySharedKey,
|
|
181
214
|
decryptBySharedKey,
|
|
215
|
+
type CryptoOptions,
|
|
182
216
|
} from 'react-native-security-suite';
|
|
183
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Pick one value per field. Unions match exported package types.
|
|
220
|
+
* Use JCA names (e.g. 'HmacSHA256', 'AES/GCM/NoPadding') for native crypto.
|
|
221
|
+
*/
|
|
222
|
+
const cryptoOptions: CryptoOptions = {
|
|
223
|
+
keyAgreementAlgorithm: 'X25519', // 'X25519' | 'ECDH'
|
|
224
|
+
keyType: 'EC', // 'OKP' | 'EC'
|
|
225
|
+
encryptionKeyAlgorithm: 'AES-256', // 'AES-256' | 'AES'
|
|
226
|
+
hmacAlgorithm: 'HmacSHA256', // 'HmacSHA256' | 'HmacSHA384' | 'HmacSHA512' | 'HMAC-SHA-256' | 'HMAC-SHA-384' | 'HMAC-SHA-512'
|
|
227
|
+
cipher: 'AES-GCM', // 'AES-GCM' | 'AES/GCM/NoPadding'
|
|
228
|
+
tagLength: 256, // GCM auth tag size in bits
|
|
229
|
+
ivLength: 12, // GCM IV size in bytes
|
|
230
|
+
};
|
|
231
|
+
|
|
184
232
|
const handleKeyExchange = async () => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const clientPublicKey = await getPublicKey();
|
|
188
|
-
console.log('Client public key:', clientPublicKey);
|
|
233
|
+
const clientPublicKey = await getPublicKey();
|
|
234
|
+
const serverPublicKey = 'SERVER_PUBLIC_KEY_FROM_API';
|
|
189
235
|
|
|
190
|
-
|
|
191
|
-
|
|
236
|
+
// Keeps the shared key in native memory (recommended)
|
|
237
|
+
await Crypto.establishSharedKey(serverPublicKey, cryptoOptions);
|
|
192
238
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
239
|
+
const encryptedMessage = await encryptBySharedKey('Secret message', cryptoOptions);
|
|
240
|
+
const decryptedMessage = await decryptBySharedKey(encryptedMessage, cryptoOptions);
|
|
241
|
+
};
|
|
242
|
+
```
|
|
196
243
|
|
|
197
|
-
|
|
198
|
-
const encryptedMessage = await encryptBySharedKey('Secret message');
|
|
199
|
-
console.log('Encrypted message:', encryptedMessage);
|
|
244
|
+
**Legacy API** (returns the derived key to JavaScript — avoid in production):
|
|
200
245
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
246
|
+
```javascript
|
|
247
|
+
import { getSharedKey } from 'react-native-security-suite';
|
|
248
|
+
|
|
249
|
+
const sharedKeyBase64 = await getSharedKey(serverPublicKey, cryptoOptions);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### Algorithm and length options
|
|
253
|
+
|
|
254
|
+
| Option | Required | Allowed values | Description |
|
|
255
|
+
|--------|----------|----------------|-------------|
|
|
256
|
+
| `keyAgreementAlgorithm` | yes | `'X25519' \| 'ECDH'` | Key-agreement algorithm passed to native `KeyAgreement` |
|
|
257
|
+
| `keyType` | yes* | `'OKP' \| 'EC'` | Key-factory algorithm for the server public key (`keyFactoryAlgorithm` alias) |
|
|
258
|
+
| `encryptionKeyAlgorithm` | yes | `'AES-256' \| 'AES'` | Symmetric key algorithm for the derived encryption key |
|
|
259
|
+
| `hmacAlgorithm` | yes* | `'HmacSHA256' \| 'HmacSHA384' \| 'HmacSHA512' \| 'HMAC-SHA-256' \| 'HMAC-SHA-384' \| 'HMAC-SHA-512'` | HMAC for HKDF and MAC key material (`hmacKeyAlgorithm` alias) |
|
|
260
|
+
| `cipher` | yes* | `'AES-GCM' \| 'AES/GCM/NoPadding'` | Cipher transformation for encrypt/decrypt (`cipherTransformation` alias) |
|
|
261
|
+
| `tagLength` | yes* | `number` (e.g. `128`) | GCM authentication tag length in **bits** (`gcmTagLength` alias) |
|
|
262
|
+
| `ivLength` | yes* | `number` (e.g. `12`) | GCM IV/nonce length in **bytes** (`gcmIvLength` alias) |
|
|
263
|
+
|
|
264
|
+
\*Provide via the preferred name or its deprecated alias (see [API Reference](#cryptooptions)).
|
|
265
|
+
|
|
266
|
+
Use **JCA-style names** on both Android and iOS (e.g. `HmacSHA256`, not `HMAC-SHA-256`). Omitting any required option throws before native code runs.
|
|
267
|
+
|
|
268
|
+
**Typical production profile (P-256 ECDH + AES-256-GCM):**
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import type { CryptoOptions } from 'react-native-security-suite';
|
|
272
|
+
|
|
273
|
+
const cryptoOptions: CryptoOptions = {
|
|
274
|
+
keyAgreementAlgorithm: 'ECDH',
|
|
275
|
+
keyType: 'EC',
|
|
276
|
+
encryptionKeyAlgorithm: 'AES',
|
|
277
|
+
hmacAlgorithm: 'HmacSHA256',
|
|
278
|
+
cipher: 'AES/GCM/NoPadding',
|
|
279
|
+
tagLength: 128,
|
|
280
|
+
ivLength: 12,
|
|
207
281
|
};
|
|
208
282
|
```
|
|
209
283
|
|
|
210
|
-
### 6.
|
|
284
|
+
### 6. JWS Generation (RFC 7515)
|
|
285
|
+
|
|
286
|
+
Generate [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515) compact JWS tokens. Supported algorithms: **HS256**, **HS384**, **HS512**. An explicit `secret` is always required.
|
|
287
|
+
|
|
288
|
+
**How it works**
|
|
289
|
+
|
|
290
|
+
1. TypeScript (`src/jws.ts`) validates `secret`, `algorithm`, and custom headers.
|
|
291
|
+
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).
|
|
292
|
+
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.
|
|
293
|
+
|
|
294
|
+
**Empty payload** (`undefined`, `null`, or `''`) produces three segments with an empty middle segment:
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
import { generateJWS } from 'react-native-security-suite';
|
|
298
|
+
|
|
299
|
+
const jws = await generateJWS({
|
|
300
|
+
algorithm: 'HS256',
|
|
301
|
+
secret: 'my-temporary-secret',
|
|
302
|
+
headers: { kid: 'key-1' },
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// compact form: <protectedHeader>.<payload>.<signature>
|
|
306
|
+
// jws.split('.').length === 3
|
|
307
|
+
// jws.split('.')[1] === '' // empty payload segment
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**JSON payload:**
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
const jws = await generateJWS({
|
|
314
|
+
algorithm: 'HS512',
|
|
315
|
+
secret: 'my-temporary-secret',
|
|
316
|
+
payload: { amount: 1000, currency: 'USD' },
|
|
317
|
+
headers: { kid: 'key-1', request_id: 'req-123', typ: 'JWS' },
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**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.
|
|
322
|
+
|
|
323
|
+
> **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.
|
|
324
|
+
|
|
325
|
+
### 7. Fetch Request Signing with JWS
|
|
326
|
+
|
|
327
|
+
Pass a `jws` object on `fetch` options. The signed token is sent as an HTTP header (default: `X-Request-Signature`).
|
|
328
|
+
|
|
329
|
+
```javascript
|
|
330
|
+
import { fetch } from 'react-native-security-suite';
|
|
331
|
+
|
|
332
|
+
await fetch('https://api.example.com/payments', {
|
|
333
|
+
method: 'POST',
|
|
334
|
+
headers: { 'Content-Type': 'application/json' },
|
|
335
|
+
body: { amount: 1000 },
|
|
336
|
+
jws: {
|
|
337
|
+
algorithm: 'HS256',
|
|
338
|
+
secret: 'temporary-session-secret',
|
|
339
|
+
headers: {
|
|
340
|
+
kid: 'key-1',
|
|
341
|
+
request_id: 'req-123',
|
|
342
|
+
timestamp: Date.now(),
|
|
343
|
+
nonce: 'unique-nonce-per-request',
|
|
344
|
+
},
|
|
345
|
+
headerName: 'X-Request-Signature',
|
|
346
|
+
detached: false,
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Default signing payload** (when `jws.payload` is omitted): native code builds a sorted JSON object from:
|
|
352
|
+
|
|
353
|
+
| Field | Source |
|
|
354
|
+
|-------|--------|
|
|
355
|
+
| `method` | HTTP method (uppercased) |
|
|
356
|
+
| `path` | URL path (`/` if empty) |
|
|
357
|
+
| `query` | Query string (if present) |
|
|
358
|
+
| `bodyHash` | Base64url SHA-256 of request body (if body present) |
|
|
359
|
+
| `timestamp`, `nonce`, `request_id` | Copied from `jws.headers` when provided |
|
|
360
|
+
|
|
361
|
+
**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.
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
jws: {
|
|
365
|
+
secret: 'temporary-session-secret',
|
|
366
|
+
payload: JSON.stringify({ amount: 1000, currency: 'USD' }),
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Detached fetch signing** — set `jws.detached: true` to send `header..signature` while the raw payload remains in the request body:
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
jws: {
|
|
374
|
+
secret: 'temporary-session-secret',
|
|
375
|
+
detached: true,
|
|
376
|
+
headers: { kid: 'key-1', request_id: 'req-123' },
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**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.
|
|
381
|
+
|
|
382
|
+
### 8. SSL Certificate Pinning
|
|
211
383
|
|
|
212
384
|
Secure your API communications with certificate pinning:
|
|
213
385
|
|
|
@@ -243,7 +415,7 @@ const secureApiCall = async () => {
|
|
|
243
415
|
};
|
|
244
416
|
```
|
|
245
417
|
|
|
246
|
-
###
|
|
418
|
+
### 9. Network Monitoring & Debugging
|
|
247
419
|
|
|
248
420
|
Monitor network requests in development:
|
|
249
421
|
|
|
@@ -274,37 +446,87 @@ const monitoredRequest = async () => {
|
|
|
274
446
|
|
|
275
447
|
### Security Detection
|
|
276
448
|
|
|
277
|
-
- `deviceHasSecurityRisk()`
|
|
449
|
+
- `deviceHasSecurityRisk()` — Detect rooted/jailbroken devices
|
|
278
450
|
|
|
279
451
|
### Encryption & Storage
|
|
280
452
|
|
|
281
|
-
- `
|
|
282
|
-
- `
|
|
283
|
-
- `SecureStorage` -
|
|
453
|
+
- `obfuscate(input, secret)` — Local obfuscation with explicit secret
|
|
454
|
+
- `deobfuscate(input, secret)` — Reverse `obfuscate`
|
|
455
|
+
- `SecureStorage` — Hardware-backed encrypted storage (`setItem`, `getItem`, `removeItem`, `getAllKeys`, `clear`, `multiGet`, `multiSet`, `multiRemove`)
|
|
456
|
+
- `encrypt(text, hardEncryption?, secretKey?)` — **deprecated**; requires `secretKey`
|
|
457
|
+
- `decrypt(encryptedText, hardEncryption?, secretKey?)` — **deprecated**; requires `secretKey`
|
|
284
458
|
|
|
285
459
|
### Key Exchange
|
|
286
460
|
|
|
287
|
-
- `getPublicKey()`
|
|
288
|
-
- `
|
|
289
|
-
- `
|
|
290
|
-
- `
|
|
461
|
+
- `Crypto.getPublicKey()` — Client public key (base64-encoded SPKI / DER)
|
|
462
|
+
- `Crypto.establishSharedKey(serverPublicKey, options)` — Derive shared key in native memory; **`options` required**
|
|
463
|
+
- `getPublicKey()` — Legacy alias for `Crypto.getPublicKey()`
|
|
464
|
+
- `getSharedKey(serverPublicKey, options)` — **Deprecated**; returns derived key to JS; **`options` required**
|
|
465
|
+
- `encryptBySharedKey(text, options)` — Encrypt with derived key; **`options` required**
|
|
466
|
+
- `decryptBySharedKey(encryptedText, options)` — Decrypt with derived key; **`options` required**
|
|
467
|
+
|
|
468
|
+
#### `CryptoOptions`
|
|
469
|
+
|
|
470
|
+
All fields below are **required** on every key-exchange and encrypt/decrypt call. There are no library defaults — define a shared `cryptoOptions` object in your app and reuse it.
|
|
471
|
+
|
|
472
|
+
| Option | Allowed values |
|
|
473
|
+
|--------|----------------|
|
|
474
|
+
| `keyAgreementAlgorithm` | `'X25519' \| 'ECDH'` |
|
|
475
|
+
| `keyType` | `'OKP' \| 'EC'` (alias: `keyFactoryAlgorithm`) |
|
|
476
|
+
| `encryptionKeyAlgorithm` | `'AES-256' \| 'AES'` |
|
|
477
|
+
| `hmacAlgorithm` | `'HmacSHA256' \| 'HmacSHA384' \| 'HmacSHA512' \| 'HMAC-SHA-256' \| 'HMAC-SHA-384' \| 'HMAC-SHA-512'` (alias: `hmacKeyAlgorithm`) |
|
|
478
|
+
| `cipher` | `'AES-GCM' \| 'AES/GCM/NoPadding'` (alias: `cipherTransformation`) |
|
|
479
|
+
| `tagLength` | `number` — GCM tag length in bits (alias: `gcmTagLength`) |
|
|
480
|
+
| `ivLength` | `number` — GCM IV length in bytes (alias: `gcmIvLength`) |
|
|
481
|
+
|
|
482
|
+
Exported types: `CryptoOptions`, `KeyAgreementAlgorithm`, `KeyType`, `EncryptionKeyAlgorithm`, `HmacAlgorithm`, `CipherAlgorithm`.
|
|
483
|
+
|
|
484
|
+
### JWS
|
|
485
|
+
|
|
486
|
+
- `generateJWS(options: GenerateJWSOptions)` — Generate compact or detached JWS
|
|
487
|
+
|
|
488
|
+
#### `GenerateJWSOptions`
|
|
489
|
+
|
|
490
|
+
| Field | Required | Description |
|
|
491
|
+
|-------|----------|-------------|
|
|
492
|
+
| `secret` | yes | Non-empty HMAC secret |
|
|
493
|
+
| `algorithm` | no | `HS256` (default), `HS384`, or `HS512` |
|
|
494
|
+
| `payload` | no | String, object, array, number, boolean, `null`, or `undefined` |
|
|
495
|
+
| `headers` | no | Custom protected headers (`kid`, `request_id`, etc.) |
|
|
496
|
+
|
|
497
|
+
#### `JwsFetchOptions` (on `fetch` options)
|
|
498
|
+
|
|
499
|
+
| Field | Required | Description |
|
|
500
|
+
|-------|----------|-------------|
|
|
501
|
+
| `secret` | yes | Non-empty HMAC secret |
|
|
502
|
+
| `algorithm` | no | `HS256` (default), `HS384`, or `HS512` |
|
|
503
|
+
| `headers` | no | Protected headers; `timestamp`/`nonce`/`request_id` also feed the default payload |
|
|
504
|
+
| `payload` | no | Explicit signing string; omit to use the default fetch payload |
|
|
505
|
+
| `detached` | no | Detached compact form (default: `false`) |
|
|
506
|
+
| `headerName` | no | Request header name (default: `X-Request-Signature`) |
|
|
507
|
+
|
|
508
|
+
Exported types: `JwsAlgorithm`, `JwsPayload`, `JwsHeaders`, `JwsHeaderValue`, `GenerateJWSOptions`, `JwsFetchOptions`.
|
|
291
509
|
|
|
292
510
|
### Network Security
|
|
293
511
|
|
|
294
|
-
- `fetch(url, options, loggerEnabled?)`
|
|
512
|
+
- `fetch(url, options, loggerEnabled?)` — Secure fetch with SSL pinning and optional JWS signing
|
|
513
|
+
|
|
514
|
+
`fetch` `options` also accepts `certificates` + `validDomains` for SSL pinning, and deprecated top-level `keyId`, `requestId`, `secret` for legacy JWS.
|
|
295
515
|
|
|
296
516
|
### UI Components
|
|
297
517
|
|
|
298
|
-
- `SecureView`
|
|
518
|
+
- `SecureView` — Screenshot-protected view component
|
|
299
519
|
|
|
300
520
|
## 🛡️ Security Best Practices
|
|
301
521
|
|
|
302
|
-
1. **Always validate certificates**
|
|
303
|
-
2. **Detect compromised devices**
|
|
304
|
-
3. **
|
|
305
|
-
4. **Protect sensitive UI**
|
|
306
|
-
5. **
|
|
307
|
-
6. **
|
|
522
|
+
1. **Always validate certificates** — Use SSL pinning for production APIs
|
|
523
|
+
2. **Detect compromised devices** — Check for root/jailbreak before sensitive operations
|
|
524
|
+
3. **Store secrets in SecureStorage** — Use hardware-backed storage for tokens and credentials
|
|
525
|
+
4. **Protect sensitive UI** — Wrap sensitive content in `SecureView`
|
|
526
|
+
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
|
|
527
|
+
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
|
|
528
|
+
7. **Monitor network traffic** — Use built-in logging for debugging only; disable in production
|
|
529
|
+
8. **Rotate session secrets** — Treat `jws.secret` as a short-lived session or request-scoped value from your backend
|
|
308
530
|
|
|
309
531
|
## 🐛 Troubleshooting
|
|
310
532
|
|
package/android/build.gradle
CHANGED
|
@@ -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
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package com.securitysuite;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cryptographic parameters supplied by the application.
|
|
7
|
+
*/
|
|
8
|
+
public final class CryptoConfig {
|
|
9
|
+
public final String keyAgreementAlgorithm;
|
|
10
|
+
public final String keyFactoryAlgorithm;
|
|
11
|
+
public final String encryptionKeyAlgorithm;
|
|
12
|
+
public final String hmacKeyAlgorithm;
|
|
13
|
+
public final String cipherTransformation;
|
|
14
|
+
public final int gcmTagLength;
|
|
15
|
+
public final int gcmIvLength;
|
|
16
|
+
|
|
17
|
+
private CryptoConfig(
|
|
18
|
+
String keyAgreementAlgorithm,
|
|
19
|
+
String keyFactoryAlgorithm,
|
|
20
|
+
String encryptionKeyAlgorithm,
|
|
21
|
+
String hmacKeyAlgorithm,
|
|
22
|
+
String cipherTransformation,
|
|
23
|
+
int gcmTagLength,
|
|
24
|
+
int gcmIvLength
|
|
25
|
+
) {
|
|
26
|
+
this.keyAgreementAlgorithm = keyAgreementAlgorithm;
|
|
27
|
+
this.keyFactoryAlgorithm = keyFactoryAlgorithm;
|
|
28
|
+
this.encryptionKeyAlgorithm = encryptionKeyAlgorithm;
|
|
29
|
+
this.hmacKeyAlgorithm = hmacKeyAlgorithm;
|
|
30
|
+
this.cipherTransformation = cipherTransformation;
|
|
31
|
+
this.gcmTagLength = gcmTagLength;
|
|
32
|
+
this.gcmIvLength = gcmIvLength;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static CryptoConfig fromReadableMap(ReadableMap options) {
|
|
36
|
+
if (options == null) {
|
|
37
|
+
throw new IllegalArgumentException("Crypto options are required");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return new CryptoConfig(
|
|
41
|
+
requireString(options, "keyAgreementAlgorithm"),
|
|
42
|
+
requireString(options, "keyFactoryAlgorithm"),
|
|
43
|
+
requireString(options, "encryptionKeyAlgorithm"),
|
|
44
|
+
requireString(options, "hmacKeyAlgorithm"),
|
|
45
|
+
requireString(options, "cipherTransformation"),
|
|
46
|
+
requireInt(options, "gcmTagLength"),
|
|
47
|
+
requireInt(options, "gcmIvLength")
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public CryptoConfig merge(ReadableMap options) {
|
|
52
|
+
return options == null ? this : fromReadableMap(mergeMaps(this, options));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private static ReadableMap mergeMaps(CryptoConfig base, ReadableMap overrides) {
|
|
56
|
+
com.facebook.react.bridge.WritableMap map = com.facebook.react.bridge.Arguments.createMap();
|
|
57
|
+
map.putString("keyAgreementAlgorithm", base.keyAgreementAlgorithm);
|
|
58
|
+
map.putString("keyFactoryAlgorithm", base.keyFactoryAlgorithm);
|
|
59
|
+
map.putString("encryptionKeyAlgorithm", base.encryptionKeyAlgorithm);
|
|
60
|
+
map.putString("hmacKeyAlgorithm", base.hmacKeyAlgorithm);
|
|
61
|
+
map.putString("cipherTransformation", base.cipherTransformation);
|
|
62
|
+
map.putInt("gcmTagLength", base.gcmTagLength);
|
|
63
|
+
map.putInt("gcmIvLength", base.gcmIvLength);
|
|
64
|
+
|
|
65
|
+
if (overrides.hasKey("keyAgreementAlgorithm")) {
|
|
66
|
+
map.putString("keyAgreementAlgorithm", overrides.getString("keyAgreementAlgorithm"));
|
|
67
|
+
}
|
|
68
|
+
if (overrides.hasKey("keyFactoryAlgorithm")) {
|
|
69
|
+
map.putString("keyFactoryAlgorithm", overrides.getString("keyFactoryAlgorithm"));
|
|
70
|
+
}
|
|
71
|
+
if (overrides.hasKey("encryptionKeyAlgorithm")) {
|
|
72
|
+
map.putString("encryptionKeyAlgorithm", overrides.getString("encryptionKeyAlgorithm"));
|
|
73
|
+
}
|
|
74
|
+
if (overrides.hasKey("hmacKeyAlgorithm")) {
|
|
75
|
+
map.putString("hmacKeyAlgorithm", overrides.getString("hmacKeyAlgorithm"));
|
|
76
|
+
}
|
|
77
|
+
if (overrides.hasKey("cipherTransformation")) {
|
|
78
|
+
map.putString("cipherTransformation", overrides.getString("cipherTransformation"));
|
|
79
|
+
}
|
|
80
|
+
if (overrides.hasKey("gcmTagLength")) {
|
|
81
|
+
map.putInt("gcmTagLength", overrides.getInt("gcmTagLength"));
|
|
82
|
+
}
|
|
83
|
+
if (overrides.hasKey("gcmIvLength")) {
|
|
84
|
+
map.putInt("gcmIvLength", overrides.getInt("gcmIvLength"));
|
|
85
|
+
}
|
|
86
|
+
return map;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private static String requireString(ReadableMap map, String key) {
|
|
90
|
+
if (!map.hasKey(key) || map.getString(key) == null) {
|
|
91
|
+
throw new IllegalArgumentException("Missing required crypto option: " + key);
|
|
92
|
+
}
|
|
93
|
+
String value = map.getString(key).trim();
|
|
94
|
+
if (value.isEmpty()) {
|
|
95
|
+
throw new IllegalArgumentException("Missing required crypto option: " + key);
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private static int requireInt(ReadableMap map, String key) {
|
|
101
|
+
if (!map.hasKey(key)) {
|
|
102
|
+
throw new IllegalArgumentException("Missing required crypto option: " + key);
|
|
103
|
+
}
|
|
104
|
+
return map.getInt(key);
|
|
105
|
+
}
|
|
106
|
+
}
|