Skip to content

Commit c1cbd9a

Browse files
authored
Merge pull request #9 from talsec/feat-add-onUnlockedDeviceDetected-and-onHardwareBackedKeystoreNotAvailableDetected
Add device state listeners to Android
2 parents 1f84e52 + a14e7c9 commit c1cbd9a

File tree

10 files changed

+82
-31
lines changed

10 files changed

+82
-31
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## 1.1.0
2+
3+
#### Android
4+
5+
- 🆕 Android now has support for device state callbacks:
6+
- 📲 **Secure Hardware Not Available**: fires when hardware-backed KeyStore is not available
7+
- 📲 **Passcode**: fires when freeRASP detects unlocked bootloader on the device
8+
9+
#### iOS
10+
11+
-`Missing Secure Enclave` detection becomes **`Secure Hardware Not Available`** to match the newly added Android callback. The functionality remains unchanged.
12+
13+
## 1.0.0
14+
15+
- Initial release of freeRASP.

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -238,22 +238,22 @@ const actions = {
238238
'device binding': () => {
239239
console.log('device binding');
240240
},
241+
// Android & iOS
242+
'secureHardwareNotAvailable': () => {
243+
console.log('secureHardwareNotAvailable');
244+
},
245+
// Android & iOS
246+
'passcode': () => {
247+
console.log('passcode');
248+
},
241249
// iOS only
242250
'deviceID': () => {
243251
console.log('deviceID');
244252
},
245253
// iOS only
246-
'missingSecureEnclave': () => {
247-
console.log('missingSecureEnclave');
248-
},
249-
// iOS only
250254
'passcodeChange': () => {
251255
console.log('passcodeChange');
252256
},
253-
// iOS only
254-
'passcode': () => {
255-
console.log('passcode');
256-
},
257257
};
258258
259259
useFreeRasp(config, actions);
@@ -290,7 +290,7 @@ The Security Report is a weekly summary describing the application's security st
290290
291291
The report provides a quick overview of the security incidents, their dynamics, app integrity, and reverse engineering attempts. It contains info about the security of devices, such as OS version or the ratio of devices with screen locks and biometrics. Each visualization also comes with a concise explanation.
292292
293-
To receive Security Reports, fill out the _watcherMail_ field in [config](#step-3-setup-the-configuration-for-your-app).
293+
To receive Security Reports, fill out the _watcherMail_ field in [config](#configuration).
294294
295295
![dashboard](https://raw.githubusercontent.com/talsec/Free-RASP-Community/master/visuals/dashboard.png)
296296
@@ -423,6 +423,7 @@ Learn more about commercial features at [https://talsec.app](https://talsec.app/
423423
<td>no</td>
424424
<td>yes</td>
425425
</tr>
426+
<tr>
426427
<td colspan=5><strong>Fair usage policy</strong></td>
427428
</tr>
428429
<tr>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.freeraspreactnative
2+
3+
import com.aheaditec.talsec_security.security.api.ThreatListener
4+
5+
internal object FreeraspDeviceStateListener: ThreatListener.DeviceState {
6+
7+
internal var listener: DeviceStateListener? = null
8+
9+
override fun onUnlockedDeviceDetected() {
10+
"unlockedDevice".let {
11+
listener?.deviceStateChangeDetected("passcode")
12+
}
13+
}
14+
15+
override fun onHardwareBackedKeystoreNotAvailableDetected() {
16+
"hardwareBackedKeystoreNotAvailable".let {
17+
listener?.deviceStateChangeDetected("secureHardwareNotAvailable")
18+
}
19+
}
20+
21+
internal interface DeviceStateListener {
22+
fun deviceStateChangeDetected(threatType: String)
23+
}
24+
25+
}

android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import com.facebook.react.bridge.ReadableMap
1212
import com.facebook.react.modules.core.DeviceEventManagerModule
1313

1414
class FreeraspReactNativeModule(val reactContext: ReactApplicationContext) :
15-
ReactContextBaseJavaModule(reactContext), ThreatListener.ThreatDetected {
15+
ReactContextBaseJavaModule(reactContext), ThreatListener.ThreatDetected, FreeraspDeviceStateListener.DeviceStateListener {
1616

17-
private val listener = ThreatListener(this)
17+
private val listener = ThreatListener(this, FreeraspDeviceStateListener)
1818

1919
override fun getName(): String {
2020
return NAME
@@ -27,6 +27,7 @@ class FreeraspReactNativeModule(val reactContext: ReactApplicationContext) :
2727

2828
try {
2929
val config = parseTalsecConfig(options)
30+
FreeraspDeviceStateListener.listener = this
3031
listener.registerListener(reactContext)
3132
Talsec.start(reactContext, config)
3233
sendOngoingPluginResult("started", null)
@@ -77,6 +78,10 @@ class FreeraspReactNativeModule(val reactContext: ReactApplicationContext) :
7778
sendOngoingPluginResult("device binding", null)
7879
}
7980

81+
override fun deviceStateChangeDetected(threatType: String) {
82+
sendOngoingPluginResult(threatType, null)
83+
}
84+
8085
private fun sendOngoingPluginResult(eventName: String, params: WritableMap?) {
8186
reactContext
8287
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)

example/src/App.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,39 +89,39 @@ const App = () => {
8989
)
9090
);
9191
},
92-
// iOS only
93-
'deviceID': () => {
92+
// Android & iOS
93+
'secureHardwareNotAvailable': () => {
9494
setAppChecks((currentState) =>
9595
currentState.map((threat) =>
96-
threat.name === 'Device ID' ? { ...threat, status: 'nok' } : threat
96+
threat.name === 'Secure Hardware Not Available'
97+
? { ...threat, status: 'nok' }
98+
: threat
9799
)
98100
);
99101
},
100-
// iOS only
101-
'missingSecureEnclave': () => {
102+
// Android & iOS
103+
'passcode': () => {
102104
setAppChecks((currentState) =>
103105
currentState.map((threat) =>
104-
threat.name === 'Missing Secure Enclave'
105-
? { ...threat, status: 'nok' }
106-
: threat
106+
threat.name === 'Passcode' ? { ...threat, status: 'nok' } : threat
107107
)
108108
);
109109
},
110110
// iOS only
111-
'passcodeChange': () => {
111+
'deviceID': () => {
112112
setAppChecks((currentState) =>
113113
currentState.map((threat) =>
114-
threat.name === 'Passcode Change'
115-
? { ...threat, status: 'nok' }
116-
: threat
114+
threat.name === 'Device ID' ? { ...threat, status: 'nok' } : threat
117115
)
118116
);
119117
},
120118
// iOS only
121-
'passcode': () => {
119+
'passcodeChange': () => {
122120
setAppChecks((currentState) =>
123121
currentState.map((threat) =>
124-
threat.name === 'Passcode' ? { ...threat, status: 'nok' } : threat
122+
threat.name === 'Passcode Change'
123+
? { ...threat, status: 'nok' }
124+
: threat
125125
)
126126
);
127127
},

example/src/checks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ export const commonChecks = [
66
{ name: 'Unofficial Store', status: 'ok' },
77
{ name: 'Hooks', status: 'ok' },
88
{ name: 'Device Binding', status: 'ok' },
9+
{ name: 'Secure Hardware Not Available', status: 'ok' },
10+
{ name: 'Passcode', status: 'ok' },
911
];
1012

1113
export const iosChecks = [
1214
{ name: 'Device ID', status: 'ok' },
13-
{ name: 'Missing Secure Enclave', status: 'ok' },
1415
{ name: 'Passcode Change', status: 'ok' },
15-
{ name: 'Passcode', status: 'ok' },
1616
];

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module '*.png';

ios/FreeraspReactNative.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,17 @@ class FreeraspReactNative: RCTEventEmitter {
4141
}
4242

4343
override func supportedEvents() -> [String]! {
44-
return ["initializationError", "started", "privilegedAccess", "debug", "simulator", "appIntegrity", "unofficialStore", "hooks", "device binding", "deviceID", "missingSecureEnclave", "passcodeChange", "passcode"]
44+
return ["initializationError", "started", "privilegedAccess", "debug", "simulator", "appIntegrity", "unofficialStore", "hooks", "device binding", "deviceID", "secureHardwareNotAvailable", "passcodeChange", "passcode"]
4545
}
4646
}
4747

4848
extension SecurityThreatCenter: SecurityThreatHandler {
4949
public func threatDetected(_ securityThreat: TalsecRuntime.SecurityThreat) {
5050
// It is better to implement security reactions (e.g. killing the app) here.
51-
FreeraspReactNative.shared!.sendEvent(withName: securityThreat.rawValue, body: securityThreat.rawValue)
51+
if (securityThreat.rawValue == "missingSecureEnclave") {
52+
FreeraspReactNative.shared!.sendEvent(withName: "secureHardwareNotAvailable", body: "secureHardwareNotAvailable")
53+
} else {
54+
FreeraspReactNative.shared!.sendEvent(withName: securityThreat.rawValue, body: securityThreat.rawValue)
55+
}
5256
}
5357
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "freerasp-react-native",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "React Native plugin for improving app security and threat monitoring on Android and iOS mobile devices.",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",

src/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ type NativeEventEmitterActions = {
2727
'hooks'?: () => any;
2828
'device binding'?: () => any;
2929
'deviceID'?: () => any;
30-
'missingSecureEnclave'?: () => any;
3130
'passcodeChange'?: () => any;
3231
'passcode'?: () => any;
32+
'secureHardwareNotAvailable'?: () => any;
3333
'started'?: () => any;
3434
'initializationError'?: (reason: { message: string }) => any;
3535
};

0 commit comments

Comments
 (0)