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.
Files changed (189) hide show
  1. package/README.md +291 -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 +106 -0
  5. package/android/src/main/java/com/securitysuite/CryptoUtils.java +155 -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 +317 -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 +73 -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 +427 -134
  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 +29 -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 +29 -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 +25 -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 +25 -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 +40 -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 +84 -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,418 @@
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;
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, cryptoConfig.hmacKeyAlgorithm);
84
+ byte[] macKeyBytes = CryptoUtils.deriveHmacKey(sharedSecret, cryptoConfig.hmacKeyAlgorithm);
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, cryptoConfig.hmacKeyAlgorithm);
118
+ byte[] macKeyBytes = CryptoUtils.deriveHmacKey(sharedSecret, cryptoConfig.hmacKeyAlgorithm);
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 = resolveCryptoConfig(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 = resolveCryptoConfig(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) {
186
+ try {
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);
213
+ } catch (Exception e) {
214
+ promise.reject("JWS_ERROR", e.getMessage(), e);
215
+ }
216
+ }
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) {
155
229
  try {
156
- String deviceId = getAndroidId();
157
- callback.invoke(deviceId, null);
230
+ promise.resolve(Obfuscation.deobfuscate(input, secret));
158
231
  } catch (Exception e) {
159
- callback.invoke(null, e);
232
+ promise.reject("DEOBFUSCATE_ERROR", e.getMessage(), e);
160
233
  }
161
234
  }
162
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;
183
264
  }
184
- String decryptedMessage = StorageEncryption.decrypt(input, key);
185
- callback.invoke(decryptedMessage, null);
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;
271
+ }
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 CryptoConfig resolveCryptoConfig(ReadableMap options) {
356
+ if (cryptoConfig != null) {
357
+ return cryptoConfig.merge(options);
358
+ }
359
+ return CryptoConfig.fromReadableMap(options);
360
+ }
361
+
362
+ private static String secureStorageMessage(Exception error) {
363
+ String message = error.getMessage();
364
+ if (message != null && !message.isEmpty()) {
365
+ return message;
366
+ }
367
+ return "Secure storage operation failed";
195
368
  }
196
369
 
197
370
  @ReactMethod
198
371
  public void fetch(String url, final ReadableMap options, Callback callback) {
199
372
  Sslpinning sslpinning = new Sslpinning(context);
200
- sslpinning.fetch(url, options, secretKey, callback);
373
+ sslpinning.fetch(url, options, callback);
374
+ }
375
+
376
+ @ReactMethod
377
+ public void getDeviceId(Callback callback) {
378
+ try {
379
+ callback.invoke(
380
+ android.provider.Settings.Secure.getString(
381
+ context.getContentResolver(),
382
+ android.provider.Settings.Secure.ANDROID_ID
383
+ ),
384
+ null
385
+ );
386
+ } catch (Exception e) {
387
+ callback.invoke(null, e.getMessage());
388
+ }
389
+ }
390
+
391
+ @ReactMethod
392
+ public void runtimeDetect(Promise promise) {
393
+ try {
394
+ promise.resolve(RuntimeDetector.detect());
395
+ } catch (Exception e) {
396
+ promise.reject("RUNTIME_DETECT_ERROR", e.getMessage(), e);
397
+ }
398
+ }
399
+
400
+ @ReactMethod
401
+ public void appIntegrityVerify(Promise promise) {
402
+ try {
403
+ promise.resolve(AppIntegrityChecker.verify(context));
404
+ } catch (Exception e) {
405
+ promise.reject("APP_INTEGRITY_ERROR", e.getMessage(), e);
406
+ }
407
+ }
408
+
409
+ @ReactMethod
410
+ public void deviceGetEnvironment(Promise promise) {
411
+ try {
412
+ promise.resolve(EmulatorDetector.detect(context));
413
+ } catch (Exception e) {
414
+ promise.reject("DEVICE_ENVIRONMENT_ERROR", e.getMessage(), e);
415
+ }
201
416
  }
202
417
 
203
418
  @ReactMethod