react-native-security-suite 0.1.2 → 0.3.0
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/LICENSE +1 -2
- package/README.md +40 -3
- package/android/build.gradle +85 -43
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +1 -3
- package/android/src/main/AndroidManifestDeprecated.xml +3 -0
- package/android/src/main/java/com/securitysuite/SecuritySuiteModule.java +202 -0
- package/android/src/main/java/com/securitysuite/SecuritySuitePackage.java +28 -0
- package/android/src/main/java/com/securitysuite/StorageEncryption.java +52 -0
- package/ios/DataHashingMethods.swift +196 -0
- package/ios/SecuritySuite-Bridging-Header.h +1 -0
- package/ios/SecuritySuite.mm +26 -0
- package/ios/SecuritySuite.swift +129 -0
- package/ios/SecuritySuite.xcodeproj/project.pbxproj +13 -17
- package/ios/SecuritySuite.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
- package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcuserdata/mohammadnavabi.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/SecuritySuite.xcodeproj/xcuserdata/mohammadnavabi.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/ios/StorageEncryption.swift +81 -0
- package/lib/commonjs/helpers.js +16 -0
- package/lib/commonjs/helpers.js.map +1 -0
- package/lib/commonjs/index.js +158 -6
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/helpers.js +9 -0
- package/lib/module/helpers.js.map +1 -0
- package/lib/module/index.js +147 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/helpers.d.ts +2 -0
- package/lib/typescript/helpers.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +20 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/package.json +54 -33
- package/react-native-security-suite.podspec +18 -2
- package/src/helpers.ts +8 -0
- package/src/index.tsx +203 -3
- package/android/src/main/java/com/reactnativesecuritysuite/SecuritySuiteModule.java +0 -38
- package/android/src/main/java/com/reactnativesecuritysuite/SecuritySuitePackage.java +0 -28
- package/ios/SecuritySuite.m +0 -12
package/LICENSE
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
4
|
-
|
|
3
|
+
Copyright (c) 2023 Mohammad Navabi
|
|
5
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
5
|
of this software and associated documentation files (the "Software"), to deal
|
|
7
6
|
in the Software without restriction, including without limitation the rights
|
package/README.md
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
# react-native-security-suite
|
|
2
2
|
|
|
3
|
-
Security
|
|
3
|
+
Security solutions for Android and iOS
|
|
4
|
+
A native implementation encryption/decryption
|
|
5
|
+
Root/Jailbreak detection
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
9
|
+
```sh
|
|
10
|
+
yarn add react-native-security-suite
|
|
11
|
+
```
|
|
12
|
+
|
|
7
13
|
```sh
|
|
8
14
|
npm install react-native-security-suite
|
|
9
15
|
```
|
|
@@ -11,11 +17,42 @@ npm install react-native-security-suite
|
|
|
11
17
|
## Usage
|
|
12
18
|
|
|
13
19
|
```js
|
|
14
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
getPublicKey,
|
|
22
|
+
getSharedKey,
|
|
23
|
+
encryptBySharedKey,
|
|
24
|
+
decryptBySharedKey,
|
|
25
|
+
encrypt,
|
|
26
|
+
decrypt,
|
|
27
|
+
deviceHasSecurityRisk,
|
|
28
|
+
} from 'react-native-security-suite';
|
|
15
29
|
|
|
16
30
|
// ...
|
|
31
|
+
const publicKey = await getPublicKey();
|
|
32
|
+
console.log('Public key: ', publicKey);
|
|
33
|
+
/*
|
|
34
|
+
* Sending the publicKey to the server and receiving the SERVER_PUBLIC_KEY
|
|
35
|
+
* Using the SERVER_PUBLIC_KEY to generate sharedKey
|
|
36
|
+
*/
|
|
37
|
+
const sharedKey = await getSharedKey('SERVER_PUBLIC_KEY');
|
|
38
|
+
console.log('Shared key: ', sharedKey);
|
|
39
|
+
// Encrypt/Decrypt by sharedKey
|
|
40
|
+
const hardEncrypted = await encryptBySharedKey('STR_FOR_ENCRYPT');
|
|
41
|
+
console.log('Encrypted result: ', hardEncrypted);
|
|
42
|
+
const hardDecrypted = await decryptBySharedKey('STR_FOR_DECRYPT');
|
|
43
|
+
console.log('Decrypted result: ', hardDecrypted);
|
|
44
|
+
|
|
45
|
+
// ------- OR --------
|
|
46
|
+
|
|
47
|
+
// Soft Encrypt/Decrypt without sharedKey
|
|
48
|
+
const softEncrypted = await encrypt('STR_FOR_ENCRYPT');
|
|
49
|
+
console.log('Encrypted result: ', softEncrypted);
|
|
50
|
+
const softDecrypted = await decrypt('STR_FOR_DECRYPT');
|
|
51
|
+
console.log('Decrypted result: ', softDecrypted);
|
|
17
52
|
|
|
18
|
-
|
|
53
|
+
// Root/Jailbreak detection
|
|
54
|
+
const isRiskyDevice = await deviceHasSecurityRisk();
|
|
55
|
+
console.log('Root/Jailbreak detection result: ', isRiskyDevice);
|
|
19
56
|
```
|
|
20
57
|
|
|
21
58
|
## Contributing
|
package/android/build.gradle
CHANGED
|
@@ -1,60 +1,102 @@
|
|
|
1
1
|
buildscript {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
classpath 'com.android.tools.build:gradle:3.5.3'
|
|
11
|
-
}
|
|
12
|
-
}
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
classpath "com.android.tools.build:gradle:7.2.1"
|
|
9
|
+
}
|
|
13
10
|
}
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
def isNewArchitectureEnabled() {
|
|
13
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
apply plugin: "com.android.library"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
if (isNewArchitectureEnabled()) {
|
|
22
|
+
apply plugin: "com.facebook.react"
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
minSdkVersion safeExtGet('SecuritySuite_minSdkVersion', 21)
|
|
25
|
-
targetSdkVersion safeExtGet('SecuritySuite_targetSdkVersion', 31)
|
|
26
|
-
versionCode 1
|
|
27
|
-
versionName "1.0"
|
|
25
|
+
def getExtOrDefault(name) {
|
|
26
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["SecuritySuite_" + name]
|
|
27
|
+
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
def getExtOrIntegerDefault(name) {
|
|
30
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["SecuritySuite_" + name]).toInteger()
|
|
31
|
+
}
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
def supportsNamespace() {
|
|
34
|
+
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
35
|
+
def major = parsed[0].toInteger()
|
|
36
|
+
def minor = parsed[1].toInteger()
|
|
37
|
+
|
|
38
|
+
// Namespace support was added in 7.3.0
|
|
39
|
+
if (major == 7 && minor >= 3) {
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return major >= 8
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
android {
|
|
47
|
+
if (supportsNamespace()) {
|
|
48
|
+
namespace "com.securitysuite"
|
|
49
|
+
} else {
|
|
50
|
+
sourceSets {
|
|
51
|
+
main {
|
|
52
|
+
manifest.srcFile "src/main/AndroidManifestDeprecated.xml"
|
|
53
|
+
}
|
|
38
54
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
58
|
+
|
|
59
|
+
defaultConfig {
|
|
60
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
61
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
62
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
63
|
+
}
|
|
64
|
+
buildTypes {
|
|
65
|
+
release {
|
|
66
|
+
minifyEnabled false
|
|
42
67
|
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
lintOptions {
|
|
71
|
+
disable "GradleCompatible"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
compileOptions {
|
|
75
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
76
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
77
|
+
}
|
|
78
|
+
|
|
43
79
|
}
|
|
44
80
|
|
|
45
81
|
repositories {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
url("$rootDir/../node_modules/react-native/android")
|
|
50
|
-
}
|
|
51
|
-
google()
|
|
52
|
-
mavenCentral()
|
|
53
|
-
jcenter()
|
|
82
|
+
mavenCentral()
|
|
83
|
+
google()
|
|
84
|
+
jcenter()
|
|
54
85
|
}
|
|
55
86
|
|
|
87
|
+
|
|
56
88
|
dependencies {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
89
|
+
// For < 0.71, this will be from the local maven repo
|
|
90
|
+
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
91
|
+
//noinspection GradleDynamicVersion
|
|
92
|
+
implementation "com.facebook.react:react-native:+"
|
|
93
|
+
implementation "com.scottyab:rootbeer-lib:0.1.0"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (isNewArchitectureEnabled()) {
|
|
97
|
+
react {
|
|
98
|
+
jsRootDir = file("../src/")
|
|
99
|
+
libraryName = "SecuritySuite"
|
|
100
|
+
codegenJavaPackageName = "com.securitysuite"
|
|
101
|
+
}
|
|
60
102
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
package com.securitysuite;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Callback;
|
|
4
|
+
import com.facebook.react.bridge.Promise;
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
8
|
+
import com.facebook.react.module.annotations.ReactModule;
|
|
9
|
+
import com.scottyab.rootbeer.RootBeer;
|
|
10
|
+
|
|
11
|
+
import androidx.annotation.NonNull;
|
|
12
|
+
|
|
13
|
+
import android.provider.Settings;
|
|
14
|
+
import android.util.Base64;
|
|
15
|
+
import android.util.Log;
|
|
16
|
+
|
|
17
|
+
import java.nio.charset.Charset;
|
|
18
|
+
import java.security.InvalidAlgorithmParameterException;
|
|
19
|
+
import java.security.InvalidKeyException;
|
|
20
|
+
import java.security.KeyFactory;
|
|
21
|
+
import java.security.KeyPair;
|
|
22
|
+
import java.security.KeyPairGenerator;
|
|
23
|
+
import java.security.NoSuchAlgorithmException;
|
|
24
|
+
import java.security.PrivateKey;
|
|
25
|
+
import java.security.PublicKey;
|
|
26
|
+
import java.security.spec.ECGenParameterSpec;
|
|
27
|
+
import java.security.spec.X509EncodedKeySpec;
|
|
28
|
+
|
|
29
|
+
import javax.crypto.BadPaddingException;
|
|
30
|
+
import javax.crypto.Cipher;
|
|
31
|
+
import javax.crypto.IllegalBlockSizeException;
|
|
32
|
+
import javax.crypto.KeyAgreement;
|
|
33
|
+
import javax.crypto.NoSuchPaddingException;
|
|
34
|
+
import javax.crypto.SecretKey;
|
|
35
|
+
import javax.crypto.spec.GCMParameterSpec;
|
|
36
|
+
import javax.crypto.spec.SecretKeySpec;
|
|
37
|
+
|
|
38
|
+
@ReactModule(name = SecuritySuiteModule.NAME)
|
|
39
|
+
public class SecuritySuiteModule extends ReactContextBaseJavaModule {
|
|
40
|
+
public static final String NAME = "SecuritySuite";
|
|
41
|
+
private ReactApplicationContext context;
|
|
42
|
+
|
|
43
|
+
KeyPairGenerator kpg;
|
|
44
|
+
KeyPair kp;
|
|
45
|
+
PublicKey publicKey;
|
|
46
|
+
PublicKey serverPublicKey;
|
|
47
|
+
PrivateKey privateKey;
|
|
48
|
+
String sharedKey;
|
|
49
|
+
SecretKey secretKey;
|
|
50
|
+
|
|
51
|
+
public SecuritySuiteModule(ReactApplicationContext reactContext) {
|
|
52
|
+
super(reactContext);
|
|
53
|
+
context = reactContext;
|
|
54
|
+
generateKeyPair();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private void generateKeyPair() {
|
|
58
|
+
try {
|
|
59
|
+
kpg = KeyPairGenerator.getInstance("EC");
|
|
60
|
+
ECGenParameterSpec prime256v1ParamSpec = new ECGenParameterSpec("secp256r1");
|
|
61
|
+
kpg.initialize(prime256v1ParamSpec);
|
|
62
|
+
kp = kpg.genKeyPair();
|
|
63
|
+
publicKey = kp.getPublic();
|
|
64
|
+
privateKey = kp.getPrivate();
|
|
65
|
+
} catch (Exception e) {
|
|
66
|
+
Log.e("generateKeyPair Error: ", String.valueOf(e));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@ReactMethod
|
|
71
|
+
public void getPublicKey(Promise promise) {
|
|
72
|
+
String base64DEREncoded = Base64.encodeToString(publicKey.getEncoded(), Base64.DEFAULT);
|
|
73
|
+
promise.resolve(base64DEREncoded);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private static SecretKey agreeSecretKey(PrivateKey prk_self, PublicKey pbk_peer, boolean lastPhase) throws Exception {
|
|
77
|
+
SecretKey desSpec;
|
|
78
|
+
try {
|
|
79
|
+
KeyAgreement keyAgree = KeyAgreement.getInstance("ECDH");
|
|
80
|
+
keyAgree.init(prk_self);
|
|
81
|
+
keyAgree.doPhase(pbk_peer, true);
|
|
82
|
+
byte[] sec = keyAgree.generateSecret();
|
|
83
|
+
desSpec = new SecretKeySpec(sec, "AES");
|
|
84
|
+
} catch (NoSuchAlgorithmException e) {
|
|
85
|
+
throw new Exception();
|
|
86
|
+
} catch (InvalidKeyException e) {
|
|
87
|
+
throw new Exception();
|
|
88
|
+
}
|
|
89
|
+
return desSpec;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@ReactMethod
|
|
93
|
+
public void getSharedKey(String serverPK, Promise promise) {
|
|
94
|
+
try {
|
|
95
|
+
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(serverPK.getBytes(), Base64.DEFAULT)); // Change ASN1 to publicKey
|
|
96
|
+
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
|
97
|
+
serverPublicKey = keyFactory.generatePublic(keySpec);
|
|
98
|
+
secretKey = agreeSecretKey(kp.getPrivate(), serverPublicKey, true);
|
|
99
|
+
sharedKey = Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT);
|
|
100
|
+
promise.resolve(sharedKey);
|
|
101
|
+
} catch (Exception e) {
|
|
102
|
+
Log.e("getSharedKey Error: ", String.valueOf(e));
|
|
103
|
+
promise.reject(String.valueOf(e));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@ReactMethod
|
|
108
|
+
public void encrypt(String input, Promise promise) {
|
|
109
|
+
try {
|
|
110
|
+
byte[] inputByte = input.getBytes();
|
|
111
|
+
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
112
|
+
byte[] decodedKey = Base64.decode(sharedKey, Base64.DEFAULT);
|
|
113
|
+
SecretKey secretKeySpec = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
|
|
114
|
+
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
|
|
115
|
+
byte[] iv = cipher.getIV();
|
|
116
|
+
assert iv.length == 12;
|
|
117
|
+
byte[] cipherText = cipher.doFinal(inputByte);
|
|
118
|
+
if (cipherText.length != inputByte.length + 16)
|
|
119
|
+
throw new IllegalStateException();
|
|
120
|
+
byte[] output = new byte[12 + inputByte.length + 16];
|
|
121
|
+
System.arraycopy(iv, 0, output, 0, 12);
|
|
122
|
+
System.arraycopy(cipherText, 0, output, 12, cipherText.length);
|
|
123
|
+
promise.resolve(Base64.encodeToString(output, Base64.NO_WRAP));
|
|
124
|
+
} catch (Exception e) {
|
|
125
|
+
Log.e("encrypt Error: ", String.valueOf(e));
|
|
126
|
+
promise.reject(String.valueOf(e));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@ReactMethod
|
|
131
|
+
public void decrypt(String input, Promise promise) {
|
|
132
|
+
try {
|
|
133
|
+
byte[] inputBytes = Base64.decode(input.getBytes(), Base64.DEFAULT);
|
|
134
|
+
if (inputBytes.length < 12 + 16) throw new IllegalArgumentException();
|
|
135
|
+
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
136
|
+
GCMParameterSpec params = new GCMParameterSpec(128, inputBytes, 0, 12);
|
|
137
|
+
byte[] decodedKey = Base64.decode(sharedKey, Base64.DEFAULT);
|
|
138
|
+
SecretKey secretKeySpec = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
|
|
139
|
+
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, params);
|
|
140
|
+
byte[] plaintext = cipher.doFinal(inputBytes, 12, inputBytes.length - 12);
|
|
141
|
+
String decrypted = new String(plaintext, Charset.forName("UTF-8"));
|
|
142
|
+
promise.resolve(decrypted);
|
|
143
|
+
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
|
|
144
|
+
promise.reject(e);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@ReactMethod
|
|
149
|
+
public void getDeviceId(Callback callback) {
|
|
150
|
+
try {
|
|
151
|
+
String deviceId = getAndroidId();
|
|
152
|
+
callback.invoke(deviceId, null);
|
|
153
|
+
} catch (Exception e) {
|
|
154
|
+
callback.invoke(null, e);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@ReactMethod
|
|
159
|
+
public void storageEncrypt(String input, String secretKey, Boolean hardEncryption, Callback callback) {
|
|
160
|
+
try {
|
|
161
|
+
String key = getAndroidId();
|
|
162
|
+
if (secretKey != null) {
|
|
163
|
+
key = secretKey;
|
|
164
|
+
}
|
|
165
|
+
String encryptedMessage = StorageEncryption.encrypt(input, key, hardEncryption);
|
|
166
|
+
callback.invoke(encryptedMessage, null);
|
|
167
|
+
} catch (Exception e) {
|
|
168
|
+
callback.invoke(null, e);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@ReactMethod
|
|
173
|
+
public void storageDecrypt(String input, String secretKey, Boolean hardEncryption, Callback callback) {
|
|
174
|
+
try {
|
|
175
|
+
String key = getAndroidId();
|
|
176
|
+
if (secretKey != null) {
|
|
177
|
+
key = secretKey;
|
|
178
|
+
}
|
|
179
|
+
String decryptedMessage = StorageEncryption.decrypt(input, key);
|
|
180
|
+
callback.invoke(decryptedMessage, null);
|
|
181
|
+
} catch (Exception e) {
|
|
182
|
+
callback.invoke(null, e.getMessage());
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private String getAndroidId() {
|
|
187
|
+
return Settings.Secure.getString(context.getContentResolver(),
|
|
188
|
+
Settings.Secure.ANDROID_ID);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@ReactMethod
|
|
192
|
+
public void deviceHasSecurityRisk(Promise promise) {
|
|
193
|
+
RootBeer rootBeer = new RootBeer(context);
|
|
194
|
+
promise.resolve(rootBeer.isRooted());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@Override
|
|
198
|
+
@NonNull
|
|
199
|
+
public String getName() {
|
|
200
|
+
return NAME;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package com.securitysuite;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.ReactPackage;
|
|
6
|
+
import com.facebook.react.bridge.NativeModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
9
|
+
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.Collections;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
|
|
14
|
+
public class SecuritySuitePackage implements ReactPackage {
|
|
15
|
+
@NonNull
|
|
16
|
+
@Override
|
|
17
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
18
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
19
|
+
modules.add(new SecuritySuiteModule(reactContext));
|
|
20
|
+
return modules;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@NonNull
|
|
24
|
+
@Override
|
|
25
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
26
|
+
return Collections.emptyList();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package com.securitysuite;
|
|
2
|
+
|
|
3
|
+
import android.util.Base64;
|
|
4
|
+
|
|
5
|
+
import java.security.SecureRandom;
|
|
6
|
+
import java.util.Arrays;
|
|
7
|
+
|
|
8
|
+
import javax.crypto.Cipher;
|
|
9
|
+
import javax.crypto.spec.IvParameterSpec;
|
|
10
|
+
import javax.crypto.spec.SecretKeySpec;
|
|
11
|
+
|
|
12
|
+
public class StorageEncryption {
|
|
13
|
+
|
|
14
|
+
public static String encrypt(String input, String encryptionKey, Boolean hardEncryption) {
|
|
15
|
+
try {
|
|
16
|
+
byte[] iv = new byte[16];
|
|
17
|
+
if (hardEncryption) {
|
|
18
|
+
new SecureRandom().nextBytes(iv);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
22
|
+
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptionKey.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
|
|
23
|
+
byte[] cipherText = cipher.doFinal(input.getBytes("utf-8"));
|
|
24
|
+
byte[] ivAndCipherText = getCombinedArray(iv, cipherText);
|
|
25
|
+
return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
|
|
26
|
+
} catch (Exception e) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static String decrypt(String encoded, String encryptionKey) {
|
|
32
|
+
try {
|
|
33
|
+
byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
|
|
34
|
+
byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
|
|
35
|
+
byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);
|
|
36
|
+
|
|
37
|
+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
38
|
+
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(encryptionKey.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
|
|
39
|
+
return new String(cipher.doFinal(cipherText), "utf-8");
|
|
40
|
+
} catch (Exception e) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private static byte[] getCombinedArray(byte[] one, byte[] two) {
|
|
46
|
+
byte[] combined = new byte[one.length + two.length];
|
|
47
|
+
for (int i = 0; i < combined.length; ++i) {
|
|
48
|
+
combined[i] = i < one.length ? one[i] : two[i - one.length];
|
|
49
|
+
}
|
|
50
|
+
return combined;
|
|
51
|
+
}
|
|
52
|
+
}
|