react-native-security-pack 1.0.1 → 2.0.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/README.md +181 -13
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +55 -60
- package/android/fix-prefab.gradle +44 -0
- package/android/gradle.properties +5 -5
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/leerman/rnsecuritypack/RNSecurityPackPackage.kt +23 -0
- package/android/src/main/java/com/margelo/nitro/rnsecuritypack/HybridSecurityPack.kt +85 -0
- package/ios/HybridSecurityPack.swift +17 -0
- package/jest/mock.js +7 -0
- package/lib/commonjs/index.js +24 -17
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/SecurityPack.nitro.js +6 -0
- package/lib/commonjs/specs/SecurityPack.nitro.js.map +1 -0
- package/lib/module/index.js +25 -18
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/SecurityPack.nitro.js +4 -0
- package/lib/module/specs/SecurityPack.nitro.js.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +7 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/specs/SecurityPack.nitro.d.ts +9 -0
- package/lib/typescript/commonjs/src/specs/SecurityPack.nitro.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +7 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/specs/SecurityPack.nitro.d.ts +9 -0
- package/lib/typescript/module/src/specs/SecurityPack.nitro.d.ts.map +1 -0
- package/nitro.json +24 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/RNSecurityPack+autolinking.cmake +81 -0
- package/nitrogen/generated/android/RNSecurityPack+autolinking.gradle +27 -0
- package/nitrogen/generated/android/RNSecurityPackOnLoad.cpp +54 -0
- package/nitrogen/generated/android/RNSecurityPackOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JHybridSecurityPackSpec.cpp +92 -0
- package/nitrogen/generated/android/c++/JHybridSecurityPackSpec.hpp +64 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnsecuritypack/HybridSecurityPackSpec.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnsecuritypack/RNSecurityPackOnLoad.kt +35 -0
- package/nitrogen/generated/ios/RNSecurityPack+autolinking.rb +62 -0
- package/nitrogen/generated/ios/RNSecurityPack-Swift-Cxx-Bridge.cpp +57 -0
- package/nitrogen/generated/ios/RNSecurityPack-Swift-Cxx-Bridge.hpp +166 -0
- package/nitrogen/generated/ios/RNSecurityPack-Swift-Cxx-Umbrella.hpp +46 -0
- package/nitrogen/generated/ios/RNSecurityPackAutolinking.mm +33 -0
- package/nitrogen/generated/ios/RNSecurityPackAutolinking.swift +26 -0
- package/nitrogen/generated/ios/c++/HybridSecurityPackSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridSecurityPackSpecSwift.hpp +92 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_std__string_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridSecurityPackSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridSecurityPackSpec_cxx.swift +170 -0
- package/nitrogen/generated/shared/c++/HybridSecurityPackSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridSecurityPackSpec.hpp +65 -0
- package/package.json +36 -42
- package/react-native-security-pack.podspec +18 -27
- package/react-native.config.js +1 -0
- package/src/index.ts +39 -0
- package/src/specs/SecurityPack.nitro.ts +7 -0
- package/android/src/main/AndroidManifestNew.xml +0 -2
- package/android/src/main/java/com/leerman/rnsecuritypack/SecurityPackModule.kt +0 -85
- package/android/src/main/java/com/leerman/rnsecuritypack/SecurityPackPackage.kt +0 -35
- package/android/src/newarch/SecurityPackSpec.kt +0 -7
- package/android/src/oldarch/SecurityPackSpec.kt +0 -12
- package/ios/SecurityPack.h +0 -12
- package/ios/SecurityPack.mm +0 -25
- package/lib/commonjs/NativeSecurityPack.js +0 -9
- package/lib/commonjs/NativeSecurityPack.js.map +0 -1
- package/lib/module/NativeSecurityPack.js +0 -5
- package/lib/module/NativeSecurityPack.js.map +0 -1
- package/lib/typescript/commonjs/src/NativeSecurityPack.d.ts +0 -8
- package/lib/typescript/commonjs/src/NativeSecurityPack.d.ts.map +0 -1
- package/lib/typescript/module/src/NativeSecurityPack.d.ts +0 -8
- package/lib/typescript/module/src/NativeSecurityPack.d.ts.map +0 -1
- package/src/NativeSecurityPack.ts +0 -9
- package/src/index.tsx +0 -43
package/README.md
CHANGED
|
@@ -1,33 +1,201 @@
|
|
|
1
1
|
# react-native-security-pack
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Библиотека для React Native с проверками безопасности на устройстве:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- **Android** — определение root и сверка SHA-1 отпечатков подписи APK
|
|
6
|
+
- **iOS** — определение jailbreak (проверка подписи APK не применима)
|
|
7
|
+
|
|
8
|
+
Реализована на [Nitro Modules](https://nitro.margelo.com/) (JSI, New Architecture). Используйте **v1** для Bridge-архитектуры RN.
|
|
9
|
+
|
|
10
|
+
## Требования
|
|
11
|
+
|
|
12
|
+
| Зависимость | Версия |
|
|
13
|
+
|-------------|--------|
|
|
14
|
+
| React Native | `>= 0.75` |
|
|
15
|
+
| New Architecture | **обязательна** (`newArchEnabled=true`) |
|
|
16
|
+
| [react-native-nitro-modules](https://github.com/mrousavy/nitro) | `>= 0.35` |
|
|
17
|
+
| iOS | `>= 15.1` |
|
|
18
|
+
| Android | `>= 24` (Android 7.0 Nougat) |
|
|
19
|
+
|
|
20
|
+
## Установка
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
yarn add react-native-security-pack react-native-nitro-modules
|
|
24
|
+
# или
|
|
25
|
+
npm install react-native-security-pack react-native-nitro-modules
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Android
|
|
29
|
+
|
|
30
|
+
В `android/gradle.properties` приложения:
|
|
31
|
+
|
|
32
|
+
```properties
|
|
33
|
+
newArchEnabled=true
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Пересоберите проект:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
cd android && ./gradlew clean
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### iOS
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
cd ios && pod install
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Убедитесь, что в Podfile включена New Architecture (по умолчанию в RN 0.76+).
|
|
49
|
+
|
|
50
|
+
## API
|
|
51
|
+
|
|
52
|
+
### `isRooted(): Promise<boolean>`
|
|
53
|
+
|
|
54
|
+
Возвращает `true`, если устройство, по мнению нативных эвристик, скомпрометировано:
|
|
55
|
+
|
|
56
|
+
- **Android** — [RootBeer Fresh](https://github.com/kimchangyoun/rootbeerFresh)
|
|
57
|
+
- **iOS** — [DTTJailbreakDetection](https://github.com/thii/DTTJailbreakDetection)
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { isRooted } from 'react-native-security-pack';
|
|
61
|
+
|
|
62
|
+
const rooted = await isRooted();
|
|
63
|
+
if (rooted) {
|
|
64
|
+
// заблокировать чувствительный сценарий
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `containsSignatures(signatures: string[]): Promise<boolean>`
|
|
69
|
+
|
|
70
|
+
Проверяет, совпадает ли **хотя бы один** переданный SHA-1 отпечаток с сертификатом, которым подписан установленный APK.
|
|
71
|
+
|
|
72
|
+
- Отпечатки можно передавать в любом регистре — сравнение нечувствительно к регистру.
|
|
73
|
+
- На **iOS** всегда возвращает `true` (у iOS нет APK-подписи; используйте другие механизмы защиты).
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { containsSignatures } from 'react-native-security-pack';
|
|
77
|
+
|
|
78
|
+
const releaseKey = '5E8F16062EA3CD2C4A0D547876BAA6F38CABF625';
|
|
79
|
+
|
|
80
|
+
const valid = await containsSignatures([releaseKey]);
|
|
81
|
+
if (!valid) {
|
|
82
|
+
// возможная пересборка / репак APK
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Как получить SHA-1 подписи
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
# debug keystore (пример)
|
|
90
|
+
keytool -list -v -keystore android/app/debug.keystore -alias androiddebugkey -storepass android -keypass android
|
|
91
|
+
|
|
92
|
+
# release (ваш keystore)
|
|
93
|
+
keytool -list -v -keystore your-release.keystore -alias your-alias
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Скопируйте значение **SHA1** без двоеточий или с ними — библиотека нормализует регистр.
|
|
97
|
+
|
|
98
|
+
## Пример
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { useEffect, useState } from 'react';
|
|
102
|
+
import { containsSignatures, isRooted } from 'react-native-security-pack';
|
|
103
|
+
|
|
104
|
+
export function SecurityCheck() {
|
|
105
|
+
const [state, setState] = useState({ rooted: false, signatureOk: false });
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
isRooted().then((rooted) => setState((s) => ({ ...s, rooted })));
|
|
109
|
+
containsSignatures(['YOUR_RELEASE_SHA1']).then((signatureOk) =>
|
|
110
|
+
setState((s) => ({ ...s, signatureOk }))
|
|
111
|
+
);
|
|
112
|
+
}, []);
|
|
113
|
+
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Запуск example-приложения из корня репозитория:
|
|
6
119
|
|
|
7
120
|
```sh
|
|
8
|
-
|
|
121
|
+
yarn install
|
|
122
|
+
yarn prepare # nitrogen + сборка JS
|
|
123
|
+
yarn example android
|
|
124
|
+
yarn example ios
|
|
9
125
|
```
|
|
10
126
|
|
|
11
|
-
##
|
|
127
|
+
## Миграция с v1
|
|
12
128
|
|
|
129
|
+
| v1 | v2 |
|
|
130
|
+
|----|-----|
|
|
131
|
+
| TurboModule / legacy bridge | Nitro Hybrid Object |
|
|
132
|
+
| `codegenConfig` в package.json | `nitro.json` + Nitrogen |
|
|
133
|
+
| Работало без New Architecture | **Только** New Architecture |
|
|
134
|
+
| Без peer `react-native-nitro-modules` | Peer-зависимость обязательна |
|
|
135
|
+
|
|
136
|
+
Публичный JS API (`isRooted`, `containsSignatures`) **не изменился**.
|
|
137
|
+
|
|
138
|
+
## Разработка библиотеки
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
yarn install
|
|
142
|
+
yarn specs # npx nitrogen — регенерация nitrogen/generated
|
|
143
|
+
yarn prepare # specs + react-native-builder-bob
|
|
144
|
+
yarn test
|
|
145
|
+
yarn typecheck
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Нативные реализации:
|
|
149
|
+
|
|
150
|
+
- `android/src/main/java/com/leerman/rnsecuritypack/HybridSecurityPack.kt`
|
|
151
|
+
- `ios/HybridSecurityPack.swift`
|
|
152
|
+
- Спека: `src/specs/SecurityPack.nitro.ts`
|
|
153
|
+
|
|
154
|
+
После изменения `.nitro.ts` снова выполните `yarn specs`.
|
|
155
|
+
|
|
156
|
+
## Тестирование (Jest)
|
|
157
|
+
|
|
158
|
+
Добавьте `moduleNameMapper` в конфигурацию Jest:
|
|
13
159
|
|
|
14
160
|
```js
|
|
15
|
-
|
|
161
|
+
// jest.config.js
|
|
162
|
+
module.exports = {
|
|
163
|
+
// ...
|
|
164
|
+
moduleNameMapper: {
|
|
165
|
+
'react-native-security-pack': 'react-native-security-pack/jest/mock',
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
По умолчанию мок возвращает:
|
|
16
171
|
|
|
17
|
-
|
|
172
|
+
| Функция | Значение |
|
|
173
|
+
|---------|----------|
|
|
174
|
+
| `isRooted()` | `false` |
|
|
175
|
+
| `containsSignatures(...)` | `true` |
|
|
18
176
|
|
|
19
|
-
|
|
177
|
+
Мок построен на `jest.fn()`, поэтому можно переопределять поведение в конкретных тестах:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { isRooted, containsSignatures } from 'react-native-security-pack';
|
|
181
|
+
|
|
182
|
+
jest.mocked(isRooted).mockResolvedValueOnce(true);
|
|
183
|
+
jest.mocked(containsSignatures).mockResolvedValueOnce(false);
|
|
20
184
|
```
|
|
21
185
|
|
|
186
|
+
## Устранение неполадок
|
|
187
|
+
|
|
188
|
+
Частые проблемы и их решения собраны в [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
|
22
189
|
|
|
23
|
-
##
|
|
190
|
+
## Ограничения
|
|
24
191
|
|
|
25
|
-
|
|
192
|
+
Проверки root/jailbreak и подписи APK — **эвристики**. Опытный атакующий может их обойти. Используйте как один слой защиты вместе с серверной валидацией, certificate pinning, Play Integrity / App Attest и т.д.
|
|
26
193
|
|
|
27
|
-
##
|
|
194
|
+
## Лицензия
|
|
28
195
|
|
|
29
|
-
MIT
|
|
196
|
+
MIT — см. [LICENSE](LICENSE).
|
|
30
197
|
|
|
31
|
-
|
|
198
|
+
## Авторы
|
|
32
199
|
|
|
33
|
-
|
|
200
|
+
Andrew Timofeev ([@leerman](https://github.com/leerman))
|
|
201
|
+
Aleksandr Nikolaevich ([@AleksandrNikolaevich](https://github.com/AleksandrNikolaevich))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
project(RNSecurityPack)
|
|
2
|
+
cmake_minimum_required(VERSION 3.9.0)
|
|
3
|
+
|
|
4
|
+
set(PACKAGE_NAME RNSecurityPack)
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
6
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
7
|
+
|
|
8
|
+
add_library(${PACKAGE_NAME} SHARED
|
|
9
|
+
src/main/cpp/cpp-adapter.cpp
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/RNSecurityPack+autolinking.cmake)
|
|
13
|
+
|
|
14
|
+
include_directories(
|
|
15
|
+
"src/main/cpp"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
find_library(LOG_LIB log)
|
|
19
|
+
|
|
20
|
+
target_link_libraries(
|
|
21
|
+
${PACKAGE_NAME}
|
|
22
|
+
${LOG_LIB}
|
|
23
|
+
android
|
|
24
|
+
)
|
package/android/build.gradle
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
buildscript {
|
|
2
|
-
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
3
|
-
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["SecurityPack_kotlinVersion"]
|
|
4
|
-
|
|
5
2
|
repositories {
|
|
6
3
|
google()
|
|
7
4
|
mavenCentral()
|
|
8
5
|
}
|
|
9
6
|
|
|
10
7
|
dependencies {
|
|
11
|
-
classpath "com.android.tools.build:gradle:7.2
|
|
12
|
-
// noinspection DifferentKotlinGradleVersion
|
|
13
|
-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
8
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
14
9
|
}
|
|
15
10
|
}
|
|
16
11
|
|
|
@@ -24,40 +19,26 @@ def isNewArchitectureEnabled() {
|
|
|
24
19
|
}
|
|
25
20
|
|
|
26
21
|
apply plugin: "com.android.library"
|
|
27
|
-
apply plugin: "kotlin
|
|
22
|
+
apply plugin: "org.jetbrains.kotlin.android"
|
|
23
|
+
apply from: "../nitrogen/generated/android/RNSecurityPack+autolinking.gradle"
|
|
24
|
+
apply from: "./fix-prefab.gradle"
|
|
28
25
|
|
|
29
26
|
if (isNewArchitectureEnabled()) {
|
|
30
27
|
apply plugin: "com.facebook.react"
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
def getExtOrDefault(name) {
|
|
34
|
-
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["
|
|
31
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["RNSecurityPack_" + name]
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
def getExtOrIntegerDefault(name) {
|
|
38
|
-
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
def supportsNamespace() {
|
|
42
|
-
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
43
|
-
def major = parsed[0].toInteger()
|
|
44
|
-
def minor = parsed[1].toInteger()
|
|
45
|
-
|
|
46
|
-
// Namespace support was added in 7.3.0
|
|
47
|
-
return (major == 7 && minor >= 3) || major >= 8
|
|
35
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RNSecurityPack_" + name]).toInteger()
|
|
48
36
|
}
|
|
49
37
|
|
|
50
38
|
android {
|
|
51
|
-
|
|
52
|
-
namespace "com.leerman.rnsecuritypack"
|
|
53
|
-
|
|
54
|
-
sourceSets {
|
|
55
|
-
main {
|
|
56
|
-
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
39
|
+
namespace "com.leerman.rnsecuritypack"
|
|
60
40
|
|
|
41
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
61
42
|
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
62
43
|
|
|
63
44
|
defaultConfig {
|
|
@@ -65,10 +46,54 @@ android {
|
|
|
65
46
|
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
66
47
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
67
48
|
|
|
49
|
+
externalNativeBuild {
|
|
50
|
+
cmake {
|
|
51
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
|
|
52
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
53
|
+
abiFilters(*reactNativeArchitectures())
|
|
54
|
+
|
|
55
|
+
buildTypes {
|
|
56
|
+
debug {
|
|
57
|
+
cppFlags "-O1 -g"
|
|
58
|
+
}
|
|
59
|
+
release {
|
|
60
|
+
cppFlags "-O2"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
externalNativeBuild {
|
|
68
|
+
cmake {
|
|
69
|
+
path "CMakeLists.txt"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
packagingOptions {
|
|
74
|
+
excludes = [
|
|
75
|
+
"META-INF",
|
|
76
|
+
"META-INF/**",
|
|
77
|
+
"**/libc++_shared.so",
|
|
78
|
+
"**/libfbjni.so",
|
|
79
|
+
"**/libjsi.so",
|
|
80
|
+
"**/libfolly_json.so",
|
|
81
|
+
"**/libfolly_runtime.so",
|
|
82
|
+
"**/libglog.so",
|
|
83
|
+
"**/libhermes.so",
|
|
84
|
+
"**/libhermes-executor-debug.so",
|
|
85
|
+
"**/libhermes_executor.so",
|
|
86
|
+
"**/libreactnative.so",
|
|
87
|
+
"**/libreactnativejni.so",
|
|
88
|
+
"**/libturbomodulejsijni.so",
|
|
89
|
+
"**/libreact_nativemodule_core.so",
|
|
90
|
+
"**/libjscexecutor.so",
|
|
91
|
+
]
|
|
68
92
|
}
|
|
69
93
|
|
|
70
94
|
buildFeatures {
|
|
71
95
|
buildConfig true
|
|
96
|
+
prefab true
|
|
72
97
|
}
|
|
73
98
|
|
|
74
99
|
buildTypes {
|
|
@@ -85,46 +110,16 @@ android {
|
|
|
85
110
|
sourceCompatibility JavaVersion.VERSION_1_8
|
|
86
111
|
targetCompatibility JavaVersion.VERSION_1_8
|
|
87
112
|
}
|
|
88
|
-
|
|
89
|
-
sourceSets {
|
|
90
|
-
main {
|
|
91
|
-
if (isNewArchitectureEnabled()) {
|
|
92
|
-
java.srcDirs += [
|
|
93
|
-
"src/newarch",
|
|
94
|
-
// Codegen specs
|
|
95
|
-
"generated/java",
|
|
96
|
-
"generated/jni"
|
|
97
|
-
]
|
|
98
|
-
} else {
|
|
99
|
-
java.srcDirs += ["src/oldarch"]
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
113
|
}
|
|
104
114
|
|
|
105
115
|
repositories {
|
|
106
116
|
mavenCentral()
|
|
107
117
|
google()
|
|
108
|
-
maven { url
|
|
118
|
+
maven { url "https://jitpack.io" }
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
112
|
-
|
|
113
121
|
dependencies {
|
|
114
|
-
// For < 0.71, this will be from the local maven repo
|
|
115
|
-
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
116
|
-
//noinspection GradleDynamicVersion
|
|
117
122
|
implementation "com.facebook.react:react-native:+"
|
|
118
|
-
implementation "
|
|
119
|
-
|
|
120
|
-
implementation 'com.github.kimchangyoun:rootbeerFresh:0.0.10'
|
|
121
|
-
// implementation 'com.scottyab:rootbeer-lib:0.1.0'
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (isNewArchitectureEnabled()) {
|
|
125
|
-
react {
|
|
126
|
-
jsRootDir = file("../src/")
|
|
127
|
-
libraryName = "SecurityPack"
|
|
128
|
-
codegenJavaPackageName = "com.leerman.rnsecuritypack"
|
|
129
|
-
}
|
|
123
|
+
implementation project(":react-native-nitro-modules")
|
|
124
|
+
implementation "com.github.kimchangyoun:rootbeerFresh:0.0.10"
|
|
130
125
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
tasks.configureEach { task ->
|
|
2
|
+
def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/
|
|
3
|
+
def matcher = task.name =~ prefabConfigurePattern
|
|
4
|
+
if (matcher.matches()) {
|
|
5
|
+
def variantName = matcher[0][1]
|
|
6
|
+
task.outputs.upToDateWhen { false }
|
|
7
|
+
task.dependsOn("externalNativeBuild${variantName}")
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
afterEvaluate {
|
|
12
|
+
def abis = reactNativeArchitectures()
|
|
13
|
+
rootProject.allprojects.each { proj ->
|
|
14
|
+
if (proj === rootProject) return
|
|
15
|
+
|
|
16
|
+
def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config ->
|
|
17
|
+
config.dependencies.any { dep ->
|
|
18
|
+
dep.group == project.group && dep.name == project.name
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (!dependsOnThisLib && proj != project) return
|
|
22
|
+
|
|
23
|
+
if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants
|
|
28
|
+
variants.all { variant ->
|
|
29
|
+
def variantName = variant.name
|
|
30
|
+
abis.each { abi ->
|
|
31
|
+
def searchDir = new File(proj.projectDir, ".cxx/${variantName}")
|
|
32
|
+
if (!searchDir.exists()) return
|
|
33
|
+
def matches = []
|
|
34
|
+
searchDir.eachDir { randomDir ->
|
|
35
|
+
def prefabFile = new File(randomDir, "${abi}/prefab_config.json")
|
|
36
|
+
if (prefabFile.exists()) matches << prefabFile
|
|
37
|
+
}
|
|
38
|
+
matches.each { prefabConfig ->
|
|
39
|
+
prefabConfig.setLastModified(System.currentTimeMillis())
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
RNSecurityPack_kotlinVersion=2.0.21
|
|
2
|
+
RNSecurityPack_minSdkVersion=24
|
|
3
|
+
RNSecurityPack_targetSdkVersion=35
|
|
4
|
+
RNSecurityPack_compileSdkVersion=35
|
|
5
|
+
RNSecurityPack_ndkVersion=27.1.12297006
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.leerman.rnsecuritypack
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
7
|
+
import com.margelo.nitro.rnsecuritypack.RNSecurityPackOnLoad
|
|
8
|
+
|
|
9
|
+
class RNSecurityPackPackage : BaseReactPackage() {
|
|
10
|
+
override fun getModule(
|
|
11
|
+
name: String,
|
|
12
|
+
reactContext: ReactApplicationContext,
|
|
13
|
+
): NativeModule? = null
|
|
14
|
+
|
|
15
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
|
|
16
|
+
ReactModuleInfoProvider { emptyMap() }
|
|
17
|
+
|
|
18
|
+
companion object {
|
|
19
|
+
init {
|
|
20
|
+
RNSecurityPackOnLoad.initializeNative()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package com.margelo.nitro.rnsecuritypack
|
|
2
|
+
|
|
3
|
+
import android.content.pm.PackageManager
|
|
4
|
+
import android.os.Build
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import androidx.annotation.Keep
|
|
7
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
8
|
+
import com.kimchangyoun.rootbeerFresh.RootBeer
|
|
9
|
+
import com.margelo.nitro.NitroModules
|
|
10
|
+
import com.margelo.nitro.core.Promise
|
|
11
|
+
import java.security.MessageDigest
|
|
12
|
+
|
|
13
|
+
@Keep
|
|
14
|
+
@DoNotStrip
|
|
15
|
+
class HybridSecurityPack : HybridSecurityPackSpec() {
|
|
16
|
+
private val context =
|
|
17
|
+
NitroModules.applicationContext
|
|
18
|
+
?: throw IllegalStateException("Android application context is not available")
|
|
19
|
+
|
|
20
|
+
override fun getSignatures(): Promise<Array<String>> {
|
|
21
|
+
Log.d(TAG, "getSignatures()")
|
|
22
|
+
return Promise.async {
|
|
23
|
+
try {
|
|
24
|
+
val packageName = context.packageName
|
|
25
|
+
val signatures =
|
|
26
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
27
|
+
val signingInfo =
|
|
28
|
+
context.packageManager.getPackageInfo(
|
|
29
|
+
packageName,
|
|
30
|
+
PackageManager.GET_SIGNING_CERTIFICATES,
|
|
31
|
+
).signingInfo
|
|
32
|
+
?: return@async emptyArray()
|
|
33
|
+
|
|
34
|
+
if (signingInfo.hasMultipleSigners()) {
|
|
35
|
+
signingInfo.apkContentsSigners
|
|
36
|
+
} else {
|
|
37
|
+
signingInfo.signingCertificateHistory
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
@Suppress("DEPRECATION")
|
|
41
|
+
context.packageManager.getPackageInfo(
|
|
42
|
+
packageName,
|
|
43
|
+
PackageManager.GET_SIGNATURES,
|
|
44
|
+
).signatures ?: emptyArray()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
signatures
|
|
48
|
+
.map { certificate ->
|
|
49
|
+
val digest = MessageDigest.getInstance("SHA")
|
|
50
|
+
digest.update(certificate.toByteArray())
|
|
51
|
+
bytesToHex(digest.digest())
|
|
52
|
+
}
|
|
53
|
+
.toTypedArray()
|
|
54
|
+
} catch (e: Exception) {
|
|
55
|
+
Log.e(TAG, "getSignatures failed", e)
|
|
56
|
+
emptyArray()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override fun isRooted(): Promise<Boolean> {
|
|
62
|
+
Log.d(TAG, "isRooted()")
|
|
63
|
+
return Promise.async {
|
|
64
|
+
RootBeer(context).isRooted
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private fun bytesToHex(bytes: ByteArray): String {
|
|
69
|
+
val hexArray = charArrayOf(
|
|
70
|
+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
71
|
+
'A', 'B', 'C', 'D', 'E', 'F',
|
|
72
|
+
)
|
|
73
|
+
val hexChars = CharArray(bytes.size * 2)
|
|
74
|
+
for (j in bytes.indices) {
|
|
75
|
+
val v = bytes[j].toInt() and 0xFF
|
|
76
|
+
hexChars[j * 2] = hexArray[v ushr 4]
|
|
77
|
+
hexChars[j * 2 + 1] = hexArray[v and 0x0F]
|
|
78
|
+
}
|
|
79
|
+
return String(hexChars)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
companion object {
|
|
83
|
+
private const val TAG = "HybridSecurityPack"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import DTTJailbreakDetection
|
|
2
|
+
import NitroModules
|
|
3
|
+
|
|
4
|
+
class HybridSecurityPack: HybridSecurityPackSpec {
|
|
5
|
+
func getSignatures() throws -> Promise<[String]> {
|
|
6
|
+
// APK signing certificates are Android-only.
|
|
7
|
+
return Promise.async {
|
|
8
|
+
[]
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
func isRooted() throws -> Promise<Bool> {
|
|
13
|
+
return Promise.async {
|
|
14
|
+
DTTJailbreakDetection.isJailbroken()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
package/jest/mock.js
ADDED
package/lib/commonjs/index.js
CHANGED
|
@@ -6,29 +6,36 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.containsSignatures = containsSignatures;
|
|
7
7
|
exports.isRooted = isRooted;
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// @ts-expect-error
|
|
15
|
-
const isTurboModuleEnabled = global.__turboModuleProxy != null;
|
|
16
|
-
const SecurityPackModule = isTurboModuleEnabled ? require('./NativeSecurityPack').default : _reactNative.NativeModules.SecurityPack;
|
|
17
|
-
const SecurityPack = SecurityPackModule ? SecurityPackModule : new Proxy({}, {
|
|
18
|
-
get() {
|
|
19
|
-
throw new Error(LINKING_ERROR);
|
|
9
|
+
var _reactNativeNitroModules = require("react-native-nitro-modules");
|
|
10
|
+
let securityPack = null;
|
|
11
|
+
function getSecurityPack() {
|
|
12
|
+
if (securityPack == null) {
|
|
13
|
+
securityPack = _reactNativeNitroModules.NitroModules.createHybridObject('SecurityPack');
|
|
20
14
|
}
|
|
21
|
-
|
|
15
|
+
return securityPack;
|
|
16
|
+
}
|
|
22
17
|
async function getSignatures() {
|
|
23
|
-
|
|
18
|
+
const signatures = await getSecurityPack().getSignatures();
|
|
19
|
+
return signatures.map(item => item.toUpperCase());
|
|
24
20
|
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks whether the app's signing certificate matches any of the given SHA-1
|
|
24
|
+
* fingerprints (hex, case-insensitive).
|
|
25
|
+
*
|
|
26
|
+
* On iOS this always returns `true` because APK signatures are Android-only.
|
|
27
|
+
*/
|
|
25
28
|
async function containsSignatures(sigs) {
|
|
26
|
-
if (_reactNative.Platform.OS === 'ios')
|
|
29
|
+
if (_reactNative.Platform.OS === 'ios') {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
27
32
|
const signatures = await getSignatures();
|
|
28
|
-
const
|
|
29
|
-
return signatures.some(item =>
|
|
33
|
+
const expected = sigs.map(item => item.toUpperCase());
|
|
34
|
+
return signatures.some(item => expected.includes(item));
|
|
30
35
|
}
|
|
36
|
+
|
|
37
|
+
/** Returns `true` when the device appears to be rooted (Android) or jailbroken (iOS). */
|
|
31
38
|
async function isRooted() {
|
|
32
|
-
return
|
|
39
|
+
return await getSecurityPack().isRooted();
|
|
33
40
|
}
|
|
34
41
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_reactNativeNitroModules","securityPack","getSecurityPack","NitroModules","createHybridObject","getSignatures","signatures","map","item","toUpperCase","containsSignatures","sigs","Platform","OS","expected","some","includes","isRooted"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,wBAAA,GAAAD,OAAA;AAGA,IAAIE,YAAqC,GAAG,IAAI;AAEhD,SAASC,eAAeA,CAAA,EAAqB;EAC3C,IAAID,YAAY,IAAI,IAAI,EAAE;IACxBA,YAAY,GACVE,qCAAY,CAACC,kBAAkB,CAAmB,cAAc,CAAC;EACrE;EACA,OAAOH,YAAY;AACrB;AAEA,eAAeI,aAAaA,CAAA,EAAsB;EAChD,MAAMC,UAAU,GAAG,MAAMJ,eAAe,CAAC,CAAC,CAACG,aAAa,CAAC,CAAC;EAC1D,OAAOC,UAAU,CAACC,GAAG,CAAEC,IAAI,IAAKA,IAAI,CAACC,WAAW,CAAC,CAAC,CAAC;AACrD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,eAAeC,kBAAkBA,CAACC,IAAc,EAAoB;EACzE,IAAIC,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;IACzB,OAAO,IAAI;EACb;EAEA,MAAMP,UAAU,GAAG,MAAMD,aAAa,CAAC,CAAC;EACxC,MAAMS,QAAQ,GAAGH,IAAI,CAACJ,GAAG,CAAEC,IAAI,IAAKA,IAAI,CAACC,WAAW,CAAC,CAAC,CAAC;EACvD,OAAOH,UAAU,CAACS,IAAI,CAAEP,IAAI,IAAKM,QAAQ,CAACE,QAAQ,CAACR,IAAI,CAAC,CAAC;AAC3D;;AAEA;AACO,eAAeS,QAAQA,CAAA,EAAqB;EACjD,OAAO,MAAMf,eAAe,CAAC,CAAC,CAACe,QAAQ,CAAC,CAAC;AAC3C","ignoreList":[]}
|