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
@@ -1,203 +1,411 @@
1
1
  package com.securitysuite;
2
2
 
3
+ import com.facebook.react.bridge.Arguments;
3
4
  import com.facebook.react.bridge.Callback;
4
5
  import com.facebook.react.bridge.Promise;
5
6
  import com.facebook.react.bridge.ReactApplicationContext;
6
7
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
7
8
  import com.facebook.react.bridge.ReactMethod;
9
+ import com.facebook.react.bridge.ReadableArray;
8
10
  import com.facebook.react.bridge.ReadableMap;
11
+ import com.facebook.react.bridge.WritableArray;
9
12
  import com.facebook.react.module.annotations.ReactModule;
10
13
 
11
14
  import androidx.annotation.NonNull;
12
15
 
13
- import android.annotation.SuppressLint;
14
- import android.provider.Settings;
15
16
  import android.util.Base64;
16
17
  import android.util.Log;
17
18
 
18
19
  import java.nio.charset.Charset;
19
- import java.security.InvalidAlgorithmParameterException;
20
- import java.security.InvalidKeyException;
20
+ import java.nio.charset.StandardCharsets;
21
21
  import java.security.KeyFactory;
22
- import java.security.KeyPair;
23
- import java.security.KeyPairGenerator;
24
- import java.security.NoSuchAlgorithmException;
25
22
  import java.security.PrivateKey;
26
23
  import java.security.PublicKey;
27
- import java.security.spec.ECGenParameterSpec;
28
24
  import java.security.spec.X509EncodedKeySpec;
29
25
 
30
- import javax.crypto.BadPaddingException;
31
26
  import javax.crypto.Cipher;
32
- import javax.crypto.IllegalBlockSizeException;
33
27
  import javax.crypto.KeyAgreement;
34
- import javax.crypto.NoSuchPaddingException;
35
28
  import javax.crypto.SecretKey;
36
29
  import javax.crypto.spec.GCMParameterSpec;
37
30
  import javax.crypto.spec.SecretKeySpec;
38
31
 
39
32
  import com.scottyab.rootbeer.RootBeer;
40
33
 
34
+ import com.securitysuite.security.AppIntegrityChecker;
35
+ import com.securitysuite.security.EmulatorDetector;
36
+ import com.securitysuite.security.RuntimeDetector;
37
+
41
38
  @ReactModule(name = SecuritySuiteModule.NAME)
42
39
  public class SecuritySuiteModule extends ReactContextBaseJavaModule {
43
40
  public static final String NAME = "SecuritySuite";
44
- private ReactApplicationContext context;
41
+ private final ReactApplicationContext context;
45
42
 
46
- KeyPairGenerator keyPairGenerator;
47
- KeyPair keyPair;
48
- PublicKey publicKey;
49
- PublicKey serverPublicKey;
50
- PrivateKey privateKey;
51
- String sharedKey;
52
- SecretKey secretKey;
43
+ private SecretKey encryptionKey;
44
+ private SecretKey hmacKey;
45
+ private CryptoConfig cryptoConfig = CryptoConfig.defaults();
53
46
 
54
47
  public SecuritySuiteModule(ReactApplicationContext reactContext) {
55
48
  super(reactContext);
56
49
  context = reactContext;
57
- generateKeyPair();
58
50
  }
59
51
 
60
- private void generateKeyPair() {
52
+ @ReactMethod
53
+ public void getPublicKey(Promise promise) {
61
54
  try {
62
- keyPairGenerator = KeyPairGenerator.getInstance("EC");
63
- ECGenParameterSpec prime256v1ParamSpec = new ECGenParameterSpec("secp256r1");
64
- keyPairGenerator.initialize(prime256v1ParamSpec);
65
- keyPair = keyPairGenerator.genKeyPair();
66
- publicKey = keyPair.getPublic();
67
- privateKey = keyPair.getPrivate();
55
+ promise.resolve(EcdhKeyStore.getPublicKeyBase64(context));
68
56
  } catch (Exception e) {
69
- Log.e("generateKeyPair Error: ", String.valueOf(e));
57
+ promise.reject("GET_PUBLIC_KEY_ERROR", e.getMessage(), e);
70
58
  }
71
59
  }
72
60
 
73
61
  @ReactMethod
74
- public void getPublicKey(Promise promise) {
75
- String base64DEREncoded = Base64.encodeToString(publicKey.getEncoded(), Base64.NO_WRAP);
76
- promise.resolve(base64DEREncoded);
77
- }
78
-
79
- private static SecretKey agreeSecretKey(PrivateKey prk_self, PublicKey pbk_peer, boolean lastPhase) throws Exception {
80
- SecretKey desSpec;
62
+ public void establishSharedKey(String serverPK, ReadableMap options, Promise promise) {
81
63
  try {
82
- KeyAgreement keyAgree = KeyAgreement.getInstance("ECDH");
83
- keyAgree.init(prk_self);
84
- keyAgree.doPhase(pbk_peer, true);
85
- byte[] sec = keyAgree.generateSecret();
86
- desSpec = new SecretKeySpec(sec, "AES");
87
- } catch (NoSuchAlgorithmException e) {
88
- throw new Exception();
89
- } catch (InvalidKeyException e) {
90
- throw new Exception();
64
+ if (serverPK == null || serverPK.trim().isEmpty()) {
65
+ promise.reject("GET_SHARED_KEY_ERROR", "Server public key is required");
66
+ return;
67
+ }
68
+
69
+ cryptoConfig = CryptoConfig.fromReadableMap(options);
70
+
71
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
72
+ Base64.decode(serverPK.trim(), Base64.NO_WRAP)
73
+ );
74
+ KeyFactory keyFactory = KeyFactory.getInstance(cryptoConfig.keyFactoryAlgorithm);
75
+ PublicKey serverPublicKey = keyFactory.generatePublic(keySpec);
76
+ PrivateKey privateKey = EcdhKeyStore.getPrivateKey(context);
77
+
78
+ KeyAgreement keyAgree = KeyAgreement.getInstance(cryptoConfig.keyAgreementAlgorithm);
79
+ keyAgree.init(privateKey);
80
+ keyAgree.doPhase(serverPublicKey, true);
81
+ byte[] sharedSecret = keyAgree.generateSecret();
82
+
83
+ byte[] encKeyBytes = CryptoUtils.deriveEncryptionKey(sharedSecret);
84
+ byte[] macKeyBytes = CryptoUtils.deriveHmacKey(sharedSecret);
85
+ encryptionKey = new SecretKeySpec(encKeyBytes, cryptoConfig.encryptionKeyAlgorithm);
86
+ hmacKey = new SecretKeySpec(macKeyBytes, cryptoConfig.hmacKeyAlgorithm);
87
+
88
+ promise.resolve(null);
89
+ } catch (Exception e) {
90
+ Log.e("establishSharedKey Error: ", String.valueOf(e));
91
+ promise.reject("GET_SHARED_KEY_ERROR", e.getMessage(), e);
91
92
  }
92
- return desSpec;
93
93
  }
94
94
 
95
95
  @ReactMethod
96
- public void getSharedKey(String serverPK, Promise promise) {
96
+ public void getSharedKey(String serverPK, ReadableMap options, Promise promise) {
97
97
  try {
98
- X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(serverPK.getBytes(), Base64.NO_WRAP)); // Change ASN1 to publicKey
99
- KeyFactory keyFactory = KeyFactory.getInstance("EC");
100
- serverPublicKey = keyFactory.generatePublic(keySpec);
101
- secretKey = agreeSecretKey(keyPair.getPrivate(), serverPublicKey, true);
102
- sharedKey = Base64.encodeToString(secretKey.getEncoded(), Base64.NO_WRAP);
103
- promise.resolve(sharedKey);
98
+ if (serverPK == null || serverPK.trim().isEmpty()) {
99
+ promise.reject("GET_SHARED_KEY_ERROR", "Server public key is required");
100
+ return;
101
+ }
102
+
103
+ cryptoConfig = CryptoConfig.fromReadableMap(options);
104
+
105
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
106
+ Base64.decode(serverPK.trim(), Base64.NO_WRAP)
107
+ );
108
+ KeyFactory keyFactory = KeyFactory.getInstance(cryptoConfig.keyFactoryAlgorithm);
109
+ PublicKey serverPublicKey = keyFactory.generatePublic(keySpec);
110
+ PrivateKey privateKey = EcdhKeyStore.getPrivateKey(context);
111
+
112
+ KeyAgreement keyAgree = KeyAgreement.getInstance(cryptoConfig.keyAgreementAlgorithm);
113
+ keyAgree.init(privateKey);
114
+ keyAgree.doPhase(serverPublicKey, true);
115
+ byte[] sharedSecret = keyAgree.generateSecret();
116
+
117
+ byte[] encKeyBytes = CryptoUtils.deriveEncryptionKey(sharedSecret);
118
+ byte[] macKeyBytes = CryptoUtils.deriveHmacKey(sharedSecret);
119
+ encryptionKey = new SecretKeySpec(encKeyBytes, cryptoConfig.encryptionKeyAlgorithm);
120
+ hmacKey = new SecretKeySpec(macKeyBytes, cryptoConfig.hmacKeyAlgorithm);
121
+
122
+ promise.resolve(Base64.encodeToString(encKeyBytes, Base64.NO_WRAP));
104
123
  } catch (Exception e) {
105
124
  Log.e("getSharedKey Error: ", String.valueOf(e));
106
- promise.reject(String.valueOf(e));
125
+ promise.reject("GET_SHARED_KEY_ERROR", e.getMessage(), e);
107
126
  }
108
127
  }
109
128
 
110
129
  @ReactMethod
111
- public void encrypt(String input, Promise promise) {
130
+ public void encrypt(String input, ReadableMap options, Promise promise) {
112
131
  try {
113
- byte[] inputByte = input.getBytes();
114
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
115
- byte[] decodedKey = Base64.decode(sharedKey, Base64.NO_WRAP);
116
- SecretKey secretKeySpec = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
117
- cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
132
+ if (encryptionKey == null) {
133
+ promise.reject("ENCRYPT_ERROR", "Encryption key not established. Call getSharedKey first.");
134
+ return;
135
+ }
136
+ CryptoConfig config = cryptoConfig.merge(options);
137
+ byte[] inputByte = input.getBytes(StandardCharsets.UTF_8);
138
+ Cipher cipher = Cipher.getInstance(config.cipherTransformation);
139
+ cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
118
140
  byte[] iv = cipher.getIV();
119
- assert iv.length == 12;
120
141
  byte[] cipherText = cipher.doFinal(inputByte);
121
- if (cipherText.length != inputByte.length + 16)
122
- throw new IllegalStateException();
123
- byte[] output = new byte[12 + inputByte.length + 16];
124
- System.arraycopy(iv, 0, output, 0, 12);
125
- System.arraycopy(cipherText, 0, output, 12, cipherText.length);
142
+ byte[] output = new byte[iv.length + cipherText.length];
143
+ System.arraycopy(iv, 0, output, 0, iv.length);
144
+ System.arraycopy(cipherText, 0, output, iv.length, cipherText.length);
126
145
  promise.resolve(Base64.encodeToString(output, Base64.NO_WRAP));
127
146
  } catch (Exception e) {
128
- Log.e("encrypt Error: ", String.valueOf(e));
129
- promise.reject(String.valueOf(e));
147
+ promise.reject("ENCRYPT_ERROR", e.getMessage(), e);
130
148
  }
131
149
  }
132
150
 
133
151
  @ReactMethod
134
- public void decrypt(String input, Promise promise) {
152
+ public void decrypt(String input, ReadableMap options, Promise promise) {
135
153
  try {
136
- byte[] inputBytes = Base64.decode(input.getBytes(), Base64.NO_WRAP);
137
- if (inputBytes.length < 12 + 16)
138
- throw new IllegalArgumentException();
139
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
140
- GCMParameterSpec params = new GCMParameterSpec(128, inputBytes, 0, 12);
141
- byte[] decodedKey = Base64.decode(sharedKey, Base64.NO_WRAP);
142
- SecretKey secretKeySpec = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
143
- cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, params);
144
- byte[] plaintext = cipher.doFinal(inputBytes, 12, inputBytes.length - 12);
145
- String decrypted = new String(plaintext, Charset.forName("UTF-8"));
146
- promise.resolve(decrypted);
147
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
148
- | BadPaddingException | InvalidAlgorithmParameterException e) {
149
- promise.reject(e);
154
+ if (encryptionKey == null) {
155
+ promise.reject("DECRYPT_ERROR", "Encryption key not established. Call getSharedKey first.");
156
+ return;
157
+ }
158
+ CryptoConfig config = cryptoConfig.merge(options);
159
+ byte[] inputBytes = Base64.decode(input.getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
160
+ int minLength = config.gcmIvLength + (config.gcmTagLength / 8);
161
+ if (inputBytes.length < minLength) {
162
+ promise.reject("DECRYPT_ERROR", "Invalid ciphertext");
163
+ return;
164
+ }
165
+ Cipher cipher = Cipher.getInstance(config.cipherTransformation);
166
+ GCMParameterSpec params = new GCMParameterSpec(
167
+ config.gcmTagLength,
168
+ inputBytes,
169
+ 0,
170
+ config.gcmIvLength
171
+ );
172
+ cipher.init(Cipher.DECRYPT_MODE, encryptionKey, params);
173
+ byte[] plaintext = cipher.doFinal(
174
+ inputBytes,
175
+ config.gcmIvLength,
176
+ inputBytes.length - config.gcmIvLength
177
+ );
178
+ promise.resolve(new String(plaintext, Charset.forName("UTF-8")));
179
+ } catch (Exception e) {
180
+ promise.reject("DECRYPT_ERROR", e.getMessage(), e);
150
181
  }
151
182
  }
152
183
 
153
184
  @ReactMethod
154
- public void getDeviceId(Callback callback) {
185
+ public void generateJWS(ReadableMap options, Promise promise) {
155
186
  try {
156
- String deviceId = getAndroidId();
157
- callback.invoke(deviceId, null);
187
+ if (options == null || !options.hasKey("secret")) {
188
+ promise.reject("JWS_ERROR", "JWS secret is required and must be a non-empty string");
189
+ return;
190
+ }
191
+
192
+ String secret = options.getString("secret");
193
+ if (secret == null || secret.trim().isEmpty()) {
194
+ promise.reject("JWS_ERROR", "JWS secret is required and must be a non-empty string");
195
+ return;
196
+ }
197
+
198
+ String payload = "";
199
+ if (options.hasKey("payload") && !options.isNull("payload")) {
200
+ payload = options.getString("payload");
201
+ if (payload == null) {
202
+ payload = "";
203
+ }
204
+ }
205
+
206
+ String algorithm = options.hasKey("algorithm") ? options.getString("algorithm") : null;
207
+ ReadableMap headers = options.hasKey("headers") ? options.getMap("headers") : null;
208
+ boolean detached = options.hasKey("detached") && options.getBoolean("detached");
209
+
210
+ JWSGenerator generator = new JWSGenerator();
211
+ String jws = generator.generate(payload, secret, algorithm, headers, detached);
212
+ promise.resolve(jws);
158
213
  } catch (Exception e) {
159
- callback.invoke(null, e);
214
+ promise.reject("JWS_ERROR", e.getMessage(), e);
160
215
  }
161
216
  }
162
217
 
218
+ @ReactMethod
219
+ public void obfuscate(String input, String secret, Promise promise) {
220
+ try {
221
+ promise.resolve(Obfuscation.obfuscate(input, secret));
222
+ } catch (Exception e) {
223
+ promise.reject("OBFUSCATE_ERROR", e.getMessage(), e);
224
+ }
225
+ }
226
+
227
+ @ReactMethod
228
+ public void deobfuscate(String input, String secret, Promise promise) {
229
+ try {
230
+ promise.resolve(Obfuscation.deobfuscate(input, secret));
231
+ } catch (Exception e) {
232
+ promise.reject("DEOBFUSCATE_ERROR", e.getMessage(), e);
233
+ }
234
+ }
235
+
236
+ /** @deprecated Use obfuscate() or SecureStorage instead. */
163
237
  @ReactMethod
164
238
  public void storageEncrypt(String input, String secretKey, Boolean hardEncryption, Callback callback) {
165
239
  try {
166
- String key = getAndroidId();
167
- if (secretKey != null) {
168
- key = secretKey;
240
+ if (secretKey == null || secretKey.trim().isEmpty()) {
241
+ callback.invoke(null, "secretKey is required. Device identifiers are not accepted as encryption keys.");
242
+ return;
169
243
  }
170
- String encryptedMessage = StorageEncryption.encrypt(input, key, hardEncryption);
171
- callback.invoke(encryptedMessage, null);
244
+ if (Boolean.TRUE.equals(hardEncryption)) {
245
+ callback.invoke(
246
+ null,
247
+ "hardEncryption is deprecated. Use SecureStorage for encrypted-at-rest data."
248
+ );
249
+ return;
250
+ }
251
+ callback.invoke(Obfuscation.obfuscate(input, secretKey), null);
172
252
  } catch (Exception e) {
173
- callback.invoke(null, e);
253
+ callback.invoke(null, e.getMessage());
174
254
  }
175
255
  }
176
256
 
257
+ /** @deprecated Use deobfuscate() or SecureStorage APIs instead. */
177
258
  @ReactMethod
178
259
  public void storageDecrypt(String input, String secretKey, Boolean hardEncryption, Callback callback) {
179
260
  try {
180
- String key = getAndroidId();
181
- if (secretKey != null) {
182
- key = secretKey;
261
+ if (secretKey == null || secretKey.trim().isEmpty()) {
262
+ callback.invoke(null, "secretKey is required. Device identifiers are not accepted as encryption keys.");
263
+ return;
264
+ }
265
+ if (Boolean.TRUE.equals(hardEncryption)) {
266
+ callback.invoke(
267
+ null,
268
+ "hardEncryption is deprecated. Use SecureStorage for encrypted-at-rest data."
269
+ );
270
+ return;
183
271
  }
184
- String decryptedMessage = StorageEncryption.decrypt(input, key);
185
- callback.invoke(decryptedMessage, null);
272
+ callback.invoke(Obfuscation.deobfuscate(input, secretKey), null);
186
273
  } catch (Exception e) {
187
274
  callback.invoke(null, e.getMessage());
188
275
  }
189
276
  }
190
277
 
191
- @SuppressLint("HardwareIds")
192
- private String getAndroidId() {
193
- return Settings.Secure.getString(context.getContentResolver(),
194
- Settings.Secure.ANDROID_ID);
278
+ @ReactMethod
279
+ public void secureStorageSetItem(String key, String value, Promise promise) {
280
+ try {
281
+ SecureStorageNative.setItem(context, key, value);
282
+ promise.resolve(null);
283
+ } catch (Exception e) {
284
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
285
+ }
286
+ }
287
+
288
+ @ReactMethod
289
+ public void secureStorageGetItem(String key, Promise promise) {
290
+ try {
291
+ promise.resolve(SecureStorageNative.getItem(context, key));
292
+ } catch (Exception e) {
293
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
294
+ }
295
+ }
296
+
297
+ @ReactMethod
298
+ public void secureStorageRemoveItem(String key, Promise promise) {
299
+ try {
300
+ SecureStorageNative.removeItem(context, key);
301
+ promise.resolve(null);
302
+ } catch (Exception e) {
303
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
304
+ }
305
+ }
306
+
307
+ @ReactMethod
308
+ public void secureStorageClear(Promise promise) {
309
+ try {
310
+ SecureStorageNative.clear(context);
311
+ promise.resolve(null);
312
+ } catch (Exception e) {
313
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
314
+ }
315
+ }
316
+
317
+ @ReactMethod
318
+ public void secureStorageGetAllKeys(Promise promise) {
319
+ try {
320
+ promise.resolve(SecureStorageNative.getAllKeys(context));
321
+ } catch (Exception e) {
322
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
323
+ }
324
+ }
325
+
326
+ @ReactMethod
327
+ public void secureStorageMultiSet(ReadableArray pairs, Promise promise) {
328
+ try {
329
+ SecureStorageNative.multiSet(context, pairs);
330
+ promise.resolve(null);
331
+ } catch (Exception e) {
332
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
333
+ }
334
+ }
335
+
336
+ @ReactMethod
337
+ public void secureStorageMultiGet(ReadableArray keys, Promise promise) {
338
+ try {
339
+ promise.resolve(SecureStorageNative.multiGet(context, keys));
340
+ } catch (Exception e) {
341
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
342
+ }
343
+ }
344
+
345
+ @ReactMethod
346
+ public void secureStorageMultiRemove(ReadableArray keys, Promise promise) {
347
+ try {
348
+ SecureStorageNative.multiRemove(context, keys);
349
+ promise.resolve(null);
350
+ } catch (Exception e) {
351
+ promise.reject("SECURE_STORAGE_ERROR", secureStorageMessage(e), e);
352
+ }
353
+ }
354
+
355
+ private static String secureStorageMessage(Exception error) {
356
+ String message = error.getMessage();
357
+ if (message != null && !message.isEmpty()) {
358
+ return message;
359
+ }
360
+ return "Secure storage operation failed";
195
361
  }
196
362
 
197
363
  @ReactMethod
198
364
  public void fetch(String url, final ReadableMap options, Callback callback) {
199
365
  Sslpinning sslpinning = new Sslpinning(context);
200
- sslpinning.fetch(url, options, secretKey, callback);
366
+ sslpinning.fetch(url, options, callback);
367
+ }
368
+
369
+ @ReactMethod
370
+ public void getDeviceId(Callback callback) {
371
+ try {
372
+ callback.invoke(
373
+ android.provider.Settings.Secure.getString(
374
+ context.getContentResolver(),
375
+ android.provider.Settings.Secure.ANDROID_ID
376
+ ),
377
+ null
378
+ );
379
+ } catch (Exception e) {
380
+ callback.invoke(null, e.getMessage());
381
+ }
382
+ }
383
+
384
+ @ReactMethod
385
+ public void runtimeDetect(Promise promise) {
386
+ try {
387
+ promise.resolve(RuntimeDetector.detect());
388
+ } catch (Exception e) {
389
+ promise.reject("RUNTIME_DETECT_ERROR", e.getMessage(), e);
390
+ }
391
+ }
392
+
393
+ @ReactMethod
394
+ public void appIntegrityVerify(Promise promise) {
395
+ try {
396
+ promise.resolve(AppIntegrityChecker.verify(context));
397
+ } catch (Exception e) {
398
+ promise.reject("APP_INTEGRITY_ERROR", e.getMessage(), e);
399
+ }
400
+ }
401
+
402
+ @ReactMethod
403
+ public void deviceGetEnvironment(Promise promise) {
404
+ try {
405
+ promise.resolve(EmulatorDetector.detect(context));
406
+ } catch (Exception e) {
407
+ promise.reject("DEVICE_ENVIRONMENT_ERROR", e.getMessage(), e);
408
+ }
201
409
  }
202
410
 
203
411
  @ReactMethod