Skip to content

Commit 893012a

Browse files
committed
Docs update: added details, improved graphs and finished reference
1 parent 341de24 commit 893012a

File tree

10 files changed

+187
-93
lines changed

10 files changed

+187
-93
lines changed

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ and **app-wise** security (tampering, recompiling, changed signature & metadata)
5353

5454

5555
## Flexibility
56-
Kevlar does not automatically detect a "standard" unsafe environment and gives a 0/1 answer.
56+
Kevlar does not automatically detect a "standard" unsafe environment and give a 0/1 answer.
5757
The kind of environment that is acceptable for your app to run in can be configured in each package individually.
5858

5959
You may be indifferent to some things (e.g. root detection) and very sensitive about others (e.g. app tampering & piracy detection).
@@ -66,14 +66,14 @@ If you don't explicitly instruct kevlar to check for a feature, then that featur
6666
## Design
6767
Each kevlar package contains custom implementations for what it has to scan for, but they all share the same overall structure, to make it easy to work with. Once you learn how to use a package, then you can transfer that knowledge to the other ones.
6868

69+
6970
``` mermaid
7071
graph LR
7172
I[Inizialization] -.Settings..-> K{Kevlar};
7273
AREQ[Attestation Requests] --> K
73-
K --> |Clear| P[Passed];
74-
K --> |Failed| NP[Not Passed];
75-
P --> ARES[Attestation Result]
76-
NP --> ARES
74+
K --> ARES[Attestation Result]
75+
ARES --> |Clear| P[Passed];
76+
ARES --> |Failed| NP[Not Passed];
7777
```
7878

7979
The founding idea is a flow of attestations. You initialize the package passing to it your settings (what you want to check for). Then you can go ahead and start requesting attestations. An attestation can either be Clear (passed) or Failed (non passed), according to your detection settings.

docs/index.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ and **app-wise** security (tampering, recompiling, changed signature & metadata)
2929

3030

3131
## Flexibility
32-
Kevlar does not automatically detect a "standard" unsafe environment and gives a 0/1 answer.
32+
Kevlar does not automatically detect a "standard" unsafe environment and give a 0/1 answer.
3333
The kind of environment that is acceptable for your app to run in can be configured in each package individually.
3434

3535
You may be indifferent to some things (e.g. root detection) and very sensitive about others (e.g. app tampering & piracy detection).
@@ -46,10 +46,9 @@ Each kevlar package contains custom implementations for what it has to scan for,
4646
graph LR
4747
I[Inizialization] -.Settings..-> K{Kevlar};
4848
AREQ[Attestation Requests] --> K
49-
K --> |Clear| P[Passed];
50-
K --> |Failed| NP[Not Passed];
51-
P --> ARES[Attestation Result]
52-
NP --> ARES
49+
K --> ARES[Attestation Result]
50+
ARES --> |Clear| P[Passed];
51+
ARES --> |Failed| NP[Not Passed];
5352
```
5453

5554
The founding idea is a flow of attestations. You initialize the package passing to it your settings (what you want to check for). Then you can go ahead and start requesting attestations. An attestation can either be Clear (passed) or Failed (non passed), according to your detection settings.

docs/pages/modules/antipiracy/antipiracy.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ graph LR
55
I[Inizialization] -.Settings..-> K{KevlarAntipiracy};
66
DB[(Dataset)] === K
77
AREQ[Attestation Requests] --> K
8-
K --> |Clear| P[Passed];
9-
K --> |Failed| NP[Not Passed];
10-
P --> ARES[AntipiracyAttestation]
11-
NP --> ARES
8+
ARES --> |Clear| P[Passed];
9+
ARES --> |Failed| NP[Not Passed];
10+
K --> ARES[AntipiracyAttestation]
1211
```
1312

1413
The antipiracy package contains tools for the detection of different categories of pirate software that may be installed and running on target devices.

docs/pages/modules/integrity/implementation.md

+16-13
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ debug check.
6969

7070
The hardcoded metadata you need to find is the following:
7171

72-
- Your application **package name**: Will check that the running binary's package name matches the
72+
- Your application **package name**: Will be used to check that the running binary's package name matches the
7373
hardcoded package name;
74-
- Your application **signature**: Will check that the running binary's signature is the same as the
74+
- Your application **signature**: Will be used to check that the running binary's signature is the same as the
7575
hardcoded signature.
7676

7777
Additionally, you should consider enabling the other two kinds of checks:
@@ -115,7 +115,7 @@ integrity.attestate(context)
115115
```
116116

117117

118-
!!! fail "Do not"
118+
!!! danger "Do not"
119119
Maybe, just maybe, you may be tempted to so something like this:
120120

121121
```kotlin title="VERY_BAD_hardcoded_metadata.kt"
@@ -136,15 +136,18 @@ For the signature, it's not so straightforward to extract because it depends on
136136
You have two different ways to get your keystore signature. In the examples we will find the debug signature, but you need to find the signature of the keystore you use to sign your application when publishing on google play.
137137

138138
#### Direct application extraction
139-
The most practical way to read your keystore it is to put the following line of code in your app, to then sign the application with the key you are interested in acquiring the signature string of, and run it.
139+
The most practical way to read your keystore it is to put the following line of code in your app (it is a courtesy method provided by kevlar to do just that), to then sign the application with the key you are interested in acquiring the signature string of, and run it.
140140

141141
```kotlin
142142
// This returns the signature of the current running application.
143143
val signature: String = KevlarIntegrity.obtainCurrentAppSignature(context)
144+
145+
// Log it to the console for extracting the signature
146+
Log.d("SIGNATURE", signature)
144147
```
145148

146149
This will output the current app signature.
147-
That's the reference string you need to give to kevlar (which will extract the runtime signature of your app and match it against that string).
150+
That's the reference string you need to give to kevlar through `hardcodedSignatures()` (which, once an attestation is requested, will extract the runtime signature of your app _(which may be tampered with, if someone recompiled your application)_ and match it against that string you just extracted).
148151

149152
!!! example "Android debug signature"
150153
Every android application is signed with some key.
@@ -154,7 +157,7 @@ That's the reference string you need to give to kevlar (which will extract the r
154157
!!! warning "Signature extraction & Google Play App Signing API"
155158
If you are using Google Play App Signing, the key you sign your application with is not the one your app is distributed with (See the [official docs](https://developer.android.com/studio/publish/app-signing) regarding the matter, and a relevant [issue](https://github.com/kevlar-kt/kevlar/issues/1) in kevlar).
156159

157-
In this case the easiest way to get your actual signature would be to upload a dummy version of your app (which logs the runtime signature) through google play store, let the backend process and sign it, download it (through the archive manager on the play console), install & run it locally on an emulator/device, and save the runtime signature. Once you have done this (quite tedious) procedure, you have your signature and can pass it to kevlar.
160+
In this case the easiest way to get your actual signature would be to upload a dummy version of your app (which logs the runtime signature) through google play store, let the backend process and sign it, download it (through the archive manager on the play console), install & run it locally on an emulator/device, and save the runtime signature. Once you have done this (quite tedious, but once-in-a-lifetime) procedure, you have your signature and can pass it to kevlar.
158161

159162
#### Android studio extraction
160163
Running `./gradlew signingReport` will spit out all the details for all the different keystores in your project.
@@ -180,22 +183,22 @@ Alias: null
180183
```
181184

182185
We then have to convert it in a string form (like that we have the raw hex bytes, we want a base64 encoding of the binary signature).
183-
In this case the conversion (you can use online tools to do this) yields `J+nqXLfuIO8B2AmhkMYHGE4jDyw=`.
186+
In my case the conversion (you can use [sha1_to_base64](https://emn178.github.io/online-tools/base64_encode.html) online tools to do this) yields `J+nqXLfuIO8B2AmhkMYHGE4jDyw=`.
184187

185-
!!! fail "Play Signing"
186-
Since we don't have access to the keystore file if we use Play Signing, this method is not viable in that case, and you have to resort to uploading a dummy version of the app, download its play-signed version through the releases page, and extract the signature from that APK file.
188+
!!! danger "Incompatible With Play Signing"
189+
Since we don't have access to the "real" keystore file if we're using use Play Signing, this method is not viable in that case, and you have to resort to uploading a dummy version of the app, download its play-signed version through the releases page, and extract the signature from that APK file.
187190

188191

189192
## Obfuscating metadata
190193
The second step (optional but recommended) is obfuscating the metadata you just gathered, so that it
191-
is **saved** in an obfuscated form (in your bytecode, so that automatic tools / unskilled attackers can't easily find it), but passed to kevlar deobfuscated (so that we have the original truth values at run time).
194+
is **stored** in an obfuscated form (in your bytecode, so that automatic tools / unskilled attackers can't easily find it), but passed to kevlar deobfuscated (obviously kevlar has to receive the real, intended value, so that we have the original truth values. The idea is to perform the deobfuscation/decryption just at run time, not leaving a trace of the actual plaintext signature in the application code).
192195

193-
This means that we ship with out app the obfuscated data and the way to convert that obfuscated data back to plaintext to feed kevlar.
196+
This means that we'll ship with our app the obfuscated data, and then at runtime (when kevlar is invoked) we will convert that obfuscated data back to plaintext to feed kevlar.
194197

195-
There are a few different ways to do it, all of them are fully implemented in the `:showcase` module:
198+
There are a few different ways to do it, all of them are fully implemented in the `:showcase` module for you check out:
196199

197200
### No obfuscation (not recommended)
198-
In this case you just save the values as they are, and pass them in `HardcodedMetadata`
201+
In this case you just save the values as they are, and pass them in `HardcodedMetadata`:
199202

200203
```kotlin title="unobfuscated_hardcoded_metadata.kt"
201204
private const val packageName = HardcodedPackageName("com.kevlar.showcase")

docs/pages/modules/integrity/integrity.md

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ graph LR
55
I[Inizialization] -.Settings..-> K{KevlarIntegrity};
66
DB([Hardcoded & Obfuscated Metadata]) === K
77
AR1[Attestation Requests] --> K
8-
K --> |Clear| P[Passed];
9-
K --> |Failed| NP[Not Passed];
10-
P --> A[IntegrityAssestation]
11-
NP --> A
8+
A --> |Clear| P[Passed];
9+
A --> |Failed| NP[Not Passed];
10+
K --> A[IntegrityAssestation]
1211
```
1312

1413
The integrity package contains tools for the detection of tampering attempts against your app.
@@ -30,7 +29,7 @@ It is capable of detecting:
3029

3130
!!! warning Automatic vs Specific attack
3231
This package is the best defense against automatic and/or unskilled attacks.
33-
If implemented well, it will kill off most of them
32+
If implemented well, it will kill off most of them.
3433

3534

3635
To [implement](implementation.md) this, you initialize `KevlarIntegrity` and provide your desired settings (which influence what is to be checked and what not). Then you can submit attestation requests (which will be executed according to your settings).
@@ -73,11 +72,11 @@ Thus there are no default configuration: you have to manually specify each item
7372
```kotlin title="Manual configuration (simplified)"
7473
private val integrity = KevlarIntegrity {
7574
checks {
76-
packageName() {
75+
packageName {
7776
// Allowed package name
7877
hardcodedPackageName("com.kevlar.showcase")
7978
}
80-
signature() {
79+
signature {
8180
// Allowed signature
8281
hardcodedSignatures("J+nqXLfuIO8B2AmhkMYHGE4jDyw=")
8382
}

docs/pages/modules/integrity/reference.md

+21-21
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# Reference
22

3-
The complete integrity configuration is as follows.
3+
The complete integrity configuration is as follows:
44

55
```kotlin title="Complete settings"
66
private val integrity = KevlarIntegrity {
77
checks {
8-
packageName() {
8+
packageName {
99
// Allowed package name
1010
hardcodedPackageName("com.kevlar.showcase")
1111
}
12-
signature() {
12+
signature {
1313
// Allowed signature
1414
hardcodedSignatures("J+nqXLfuIO8B2AmhkMYHGE4jDyw=")
1515
}
@@ -38,11 +38,11 @@ Once kevlar has all the required data it is able to differentiate between genuin
3838
```kotlin hl_lines="3-6"
3939
private val integrity = KevlarIntegrity {
4040
checks {
41-
packageName() {
41+
packageName {
4242
// Allowed package name
4343
hardcodedPackageName("com.kevlar.showcase")
4444
}
45-
signature() {
45+
signature {
4646
// Allowed signature
4747
hardcodedSignatures("J+nqXLfuIO8B2AmhkMYHGE4jDyw=")
4848
}
@@ -53,24 +53,25 @@ private val integrity = KevlarIntegrity {
5353
}
5454
```
5555

56-
You can find instruction on where to find the right parameters in [implementation](implementation.md).
56+
You can find instruction on how to derive the right parameters for your app in [implementation](implementation.md).
5757
In this case you simply have to pass in the package name of your app, so kevlar knows what is the right package.
5858

5959

6060
## Signature check
6161
The `signature()` function tells kevlar to enable the integrity checks for the application signature.
6262

6363
This is a parametric setting, since kevlar needs to know what is the "right" application signature is.
64-
Once kevlar has all the required data it is able to differentiate between genuine and tampered binaries.
64+
65+
Once kevlar has all the required data, it is able to differentiate between genuine and tampered binaries (by checking the hardcoded data against the runtime-provided information).
6566

6667
```kotlin hl_lines="7-10"
6768
private val integrity = KevlarIntegrity {
6869
checks {
69-
packageName() {
70+
packageName {
7071
// Allowed package name
7172
hardcodedPackageName("com.kevlar.showcase")
7273
}
73-
signature() {
74+
signature {
7475
// Allowed signature
7576
hardcodedSignatures("J+nqXLfuIO8B2AmhkMYHGE4jDyw=")
7677
}
@@ -81,7 +82,7 @@ private val integrity = KevlarIntegrity {
8182
}
8283
```
8384

84-
You can find instruction on where to find the right parameters in [implementation](implementation.md).
85+
You can find instruction on how to derive the right parameters for your app in [implementation](implementation.md).
8586

8687

8788

@@ -91,11 +92,11 @@ The `debug()` function tells kevlar to enable integrity debug checks.
9192
```kotlin hl_lines="12"
9293
private val integrity = KevlarIntegrity {
9394
checks {
94-
packageName() {
95+
packageName {
9596
// Allowed package name
9697
hardcodedPackageName("com.kevlar.showcase")
9798
}
98-
signature() {
99+
signature {
99100
// Allowed signature
100101
hardcodedSignatures("J+nqXLfuIO8B2AmhkMYHGE4jDyw=")
101102
}
@@ -106,33 +107,32 @@ private val integrity = KevlarIntegrity {
106107
}
107108
```
108109

109-
If debug flags are found on your application it will be reported.
110+
If any debug flag is found on your application, it will be reported.
110111

111112

112113
## Installer check
113114
The `installer()` function tells kevlar to enable installer checks.
114115

115-
Since android R, google introduced APIs to check the original installer of a certain package.
116-
With this check, you can instruct kevlar to analyze that installer (if available) and detect
117-
whether it is allowed or not by your security policy.
116+
Since android R, google introduced new APIs to check for the original installer of a certain package.
117+
118+
With this check, you can instruct kevlar to analyze (if available) which software installed your application, and detect whether it is allowed or not by your security policy.
118119

119-
In this case, the only allowed installer package is the Google Play Store, but you can always
120-
add more through the `allowInstaller` function.
120+
In this case, the only allowed installer package is the Google Play Store, but you can always add more (whitelist) through the `allowInstaller` function.
121121

122122
```kotlin hl_lines="13-15"
123123
private val integrity = KevlarIntegrity {
124124
checks {
125-
packageName() {
125+
packageName {
126126
// Allowed package name
127127
hardcodedPackageName("com.kevlar.showcase")
128128
}
129-
signature() {
129+
signature {
130130
// Allowed signature
131131
hardcodedSignatures("J+nqXLfuIO8B2AmhkMYHGE4jDyw=")
132132
}
133133

134134
debug()
135-
installer() {
135+
installer {
136136
allowInstaller("com.sec.android.app.samsungapps")
137137
}
138138
}

docs/pages/modules/rooting/implementation.md

+5-27
Original file line numberDiff line numberDiff line change
@@ -59,37 +59,15 @@ We go ahead and create a working single-attestation example (for system modifica
5959

6060
## Configuration
6161
As we said, the kinds of checks you can run are divided in two different categories, `targets` and `status`.
62-
The first is to check for eventual system modification, the former to check for eventual in-system status.
62+
The first is to check for system modification, the former to check for eventual in-system status.
6363

64-
The following complete configuration runs every check that kevlar disposes.
64+
The following is the default (most commonly chosen) configuration.
6565

66-
In details:
67-
68-
- `flagPermissive()`, if enabled, will report `DetectableSystemStatus.SELINUX` also if selinux status is set to permissive status (which is a stricter criteria), while by default it will only trip if selinux is disabled;
69-
- `allowExplicitRootCheck()`, if enabled, will use more aggressive checks to determine if any of the required targets is installed, including explicitly trying to acquire root access.
70-
71-
72-
```kotlin
73-
private val rooting = KevlarRooting {
74-
targets {
75-
root()
76-
magisk()
77-
busybox()
78-
xposed()
79-
}
80-
81-
allowExplicitRootCheck()
82-
83-
status {
84-
testKeys()
85-
emulator()
86-
selinux {
87-
flagPermissive()
88-
}
89-
}
90-
}
66+
```kotlin title="Automatic settings"
67+
private val antipiracy = KevlarRooting.Defaults.Standard()
9168
```
9269

70+
You can find more information for configuring the rooting module configuration in the [reference](reference.md).
9371

9472
## In-Place
9573
This is the most concise way to implement rooting.

docs/pages/modules/rooting/internals.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,4 @@ Xposed framework requires a specific check on a system file to determine whether
1212

1313
## Notes
1414
The `rooting` module relies on [libsu](https://github.com/topjohnwu/libsu) for shell command execution.
15-
This may be a debatable choice, but it is one of the only well-written libraries and it works both efficiently and reliably.
16-
The alternative would have been implementing a custom shell execution mechanism, which is not hard to do, but it is hard to do *well*.
15+
This may be a debatable choice, but it is one of the only well-written libraries and it works both efficiently and reliably.

0 commit comments

Comments
 (0)