Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix asset encryption and update buildscript #96

Open
wants to merge 11 commits into
base: kt-n
Choose a base branch
from
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ Disable activity launch on theme:
## Step 1: Package Naming
The FIRST thing you need to change is the package identifier (the name the app identifies as) to something more meaningful to you. Open up [build.gradle.kts](app/build.gradle.kts) and look for this line
```kt
applicationId("substratum.theme.template")
applicationId = "substratum.theme.template"
```
Change this to anything you want, for instance:
```kt
applicationId("com.yourname.themename")
applicationId = "com.yourname.themename"
```

Change Package Name in the project structure (optional):
Expand Down Expand Up @@ -130,26 +130,26 @@ If you take a look at the aforementioned theme_configurations.xml, you will see
## Step 6: Safeguard your theme! Don't let the pirates win!

### If you want to enable the Substratum theme for other Theme Managers (e.g. Slim)
In ThemerConstants.gradle, change the [SUPPORTS_THIRD_PARTY_SYSTEMS](app/ThemerConstants.gradle#L9) on line 9.
In ThemerConstants.kt, change the [SUPPORTS_THIRD_PARTY_SYSTEMS](buildSrc/src/main/kotlin/ThemerConstants.kt#L8) on line 8.

### If you don't want to activate AntiPiracy
Then you can stop reading and get your theme published! Good luck!

### Getting started with AntiPiracy

If you are ready to get AntiPiracy set up, all you need to look at is [ThemerConstants.gradle](app/ThemerConstants.gradle)!
If you are ready to get AntiPiracy set up, all you need to look at is [ThemerConstants.kt](buildSrc/src/main/kotlin/ThemerConstants.kt)!

Compile your theme as a SIGNED release APK from Android Studio (Build -> Generate Signed APK). Then launch the signed apk on your device and your log will spit out an error log under the name "SubstratumThemeReport", and you want to copy and paste that into [APK_SIGNATURE_PRODUCTION](app/ThemerConstants.gradle#L13) on line 13.
Compile your theme as a SIGNED release APK from Android Studio (Build -> Generate Signed APK). Then launch the signed apk on your device and your log will spit out an error log under the name "SubstratumThemeReport", and you want to copy and paste that into [APK_SIGNATURE_PRODUCTION](buildSrc/src/main/kotlin/ThemerConstants.kt#L12) on line 12.

**NOTE**: If you are planning to make use of [Google Play App Signing](https://developer.android.com/studio/publish/app-signing.html#google-play-app-signing), **DO NOT** fill the `APK_SIGNATURE_PRODUCTION` field in [ThemerConstants.gradle](app/ThemerConstants.gradle).
**NOTE**: If you are planning to make use of [Google Play App Signing](https://developer.android.com/studio/publish/app-signing.html#google-play-app-signing), **DO NOT** fill the `APK_SIGNATURE_PRODUCTION` field in [ThemerConstants.kt](buildSrc/src/main/kotlin/ThemerConstants.kt).

Then you would need to go to Play Developer Console. Then access to your app -> Services and APIs, generate a new API key for your app and then paste it into [BASE_64_LICENSE_KEY](app/ThemerConstants.gradle#L12) on line 12.
Then you would need to go to Play Developer Console. Then access to your app -> Services and APIs, generate a new API key for your app and then paste it into [BASE_64_LICENSE_KEY](buildSrc/src/main/kotlin/ThemerConstants.kt#L11) on line 12.

Third, if you would like to change where it checks for various things such as Amazon App Store Enforcement or Play Store Enforcement, you have options listed on line 16 and lines below it, simply change from `true` to `false` and vice versa to make your desired configuration.

Finally, if you would like to enable intensive mode anti-piracy (App package blacklist), add as many package names as you want under [BLACKLISTED_APPLICATIONS](app/src/main/kotlin/substratum/theme/template/AdvancedConstants.kt#L12) on line 12. Then make sure to enable [ENABLE_APP_BLACKLIST_CHECK](app/ThemerConstants.gradle#L16) on line 16.
Finally, if you would like to enable intensive mode anti-piracy (App package blacklist), add as many package names as you want under [BLACKLISTED_APPLICATIONS](app/src/main/kotlin/substratum/theme/template/AdvancedConstants.kt#L12) on line 12. Then make sure to enable [ENABLE_APP_BLACKLIST_CHECK](buildSrc/src/main/kotlin/ThemerConstants.kt#L15) on line 15.

**Under no circumstances should you share your ThemerConstants.gradle file, unless specifically asked by an [official substratum developer](https://github.com/substratum/documentation#team-info-and-responsibilities)**!
**Under no circumstances should you share your ThemerConstants.kt file, unless specifically asked by an [official substratum developer](https://github.com/substratum/documentation#team-info-and-responsibilities)**!

### Encrypted Assets
As of template version 11.0.0, all theme assets are duplicated are encrypted within the APK by default, not your original assets!
Expand All @@ -159,14 +159,14 @@ Always use a version control tool listed below to host your private themes!
BitBucket: https://bitbucket.org/
GitLab: https://about.gitlab.com/

If you want to keep your theme assets unencrypted, just change the value [here](app/ThemerConstants.gradle#L4) to false.
If you want to keep your theme assets unencrypted, just change the value [here](buildSrc/src/main/kotlin/ThemerConstants.kt#L3) to false.

### Enforcing security
As of template version 11.0.0, themes have an additional check on the build of substratum your users should be running.

What this means is that themes can ensure their themes ONLY function with our full release cycle with debug and Play Store releases.

If you would like to enable this feature (only allow your theme to be used with official substratum builds), all you have to do is to flip `true` to `false` [here](app/ThemerConstants.gradle#L20)!
If you would like to enable this feature (only allow your theme to be used with official substratum builds), all you have to do is to flip `true` to `false` [here](buildSrc/src/main/kotlin/ThemerConstants.kt#L19)!

### Now what?
Nothing. Now you're set to publish your theme!
Expand Down
121 changes: 38 additions & 83 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,38 @@ import ThemerConstants.ENFORCE_GOOGLE_PLAY_INSTALL
import ThemerConstants.SHOULD_ENCRYPT_ASSETS
import ThemerConstants.SUPPORTS_THIRD_PARTY_SYSTEMS

import java.util.Random
import Util.assets
import Util.cleanEncryptedAssets
import Util.copyEncryptedTo
import Util.generateRandomByteArray
import Util.tempAssets
import Util.twistAsset

import java.io.FileInputStream
import java.io.FileOutputStream

import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import javax.crypto.spec.IvParameterSpec

plugins {
id("com.android.application")
id("kotlin-android")
kotlin("android")
}

val key = ByteArray(16).apply {
Random().nextBytes(this)
}

val ivKey = ByteArray(16).apply {
Random().nextBytes(this)
}
// Themers: DO NOT MODIFY
val secretKey = generateRandomByteArray()
val ivKey = generateRandomByteArray()

android {
compileSdkVersion(30)
compileSdk = 31

defaultConfig {
// If you're planning to change up the package name, ensure you have read the readme
// thoroughly!
applicationId("substratum.theme.template")
applicationId = "substratum.theme.template"
// We are only supporting Nougat and above, all new changes will incorporate Nougat changes
// to the substratum repo rather than anything lower. Keep targetSdkVersion the same.
minSdkVersion(24)
minSdk = 24
// Both versions must be changed to increment on Play Store/user's devices
versionCode = 2
versionName = "2.0"
Expand All @@ -45,8 +46,7 @@ android {
buildConfigField("boolean", "SUPPORTS_THIRD_PARTY_SYSTEMS", "$SUPPORTS_THIRD_PARTY_SYSTEMS")
buildConfigField("boolean", "ENABLE_APP_BLACKLIST_CHECK", "$ENABLE_APP_BLACKLIST_CHECK")
buildConfigField("boolean", "ALLOW_THIRD_PARTY_SUBSTRATUM_BUILDS", "$ALLOW_THIRD_PARTY_SUBSTRATUM_BUILDS")
buildConfigField("String", "IV_KEY", "\"$ivKey\"")
buildConfigField("byte[]", "DECRYPTION_KEY", key.joinToString(prefix = "{", postfix = "}"))
buildConfigField("byte[]", "DECRYPTION_KEY", secretKey.joinToString(prefix = "{", postfix = "}"))
buildConfigField("byte[]", "IV_KEY", ivKey.joinToString(prefix = "{", postfix = "}"))
resValue("string", "encryption_status", if (shouldEncrypt()) "onCompileVerify" else "false")
}
Expand Down Expand Up @@ -78,10 +78,10 @@ android {
}

dependencies {
//implementation(fileTree(include = ["*.jar"], dir = "libs"))
implementation(kotlin("stdlib-jdk8", version = Constants.kotlinVersion))

implementation("androidx.appcompat:appcompat:1.4.1")
implementation("com.github.javiersantos:PiracyChecker:1.2.5")
implementation(kotlin("stdlib-jdk8"))
implementation("androidx.appcompat:appcompat:1.2.0")
}

// Themers: DO NOT MODIFY ANYTHING BELOW
Expand All @@ -91,53 +91,28 @@ tasks.register("encryptAssets") {
return@register
}

val tempAssets = File(projectDir, "/src/main/assets-temp")
if (!tempAssets.exists()) {
// Check if temp assets exist
if (!projectDir.tempAssets.exists()) {
println("Encrypting duplicated assets, don't worry, your original assets are safe...")
val list = mutableListOf<File>()
val dir = File(projectDir, "/src/main/assets")
dir.listFiles()?.filter { it.isFile }?.forEach { file ->
list.add(file)

val fis = FileInputStream(file)
val fo = File(file.absolutePath.replace("assets", "assets-temp"))
fo.parentFile.mkdirs()
val fos = FileOutputStream(fo)
val buffer = ByteArray(4096)
var n: Int
while (fis.read(buffer).also { n = it } != -1) {
fos.write(buffer, 0, n)

val secretKeySpec = SecretKeySpec(secretKey, "AES")
val ivParameterSpec = IvParameterSpec(ivKey)
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
.apply {
init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
}
fis.close()
fos.close()
}

list.forEach { file ->
// Encrypt every single file in the assets dir recursively
projectDir.assets.walkTopDown().filter { it.isFile }.forEach { file ->
file.twistAsset("assets", "assets-temp")

//Encrypt assets
if (file.absolutePath.contains("overlays")) {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
val secret = SecretKeySpec(key, "AES")
val iv = IvParameterSpec(ivKey)

cipher.init(Cipher.ENCRYPT_MODE, secret, iv)
val fis = FileInputStream(file)
val fos = FileOutputStream(file.absolutePath + ".enc")

val input = ByteArray(64)
var bytesRead: Int
while (fis.read(input).also {bytesRead = it } != -1) {
val output = cipher.update(input, 0, bytesRead)
if (output != null) {
fos.write(output)
FileInputStream(file).use { fis ->
FileOutputStream("${file.absolutePath}.enc").use { fos ->
fis.copyEncryptedTo(fos, cipher, bufferSize = 64)
}
}
val output = cipher.doFinal()
if (output != null) {
fos.write(output)
}
fis.close()
fos.flush()
fos.close()

file.delete()
}
}
Expand All @@ -147,36 +122,16 @@ tasks.register("encryptAssets") {
}

project.afterEvaluate {
tasks.named("preBuild"){
tasks.named("preBuild") {
dependsOn("encryptAssets")
}
}

gradle.buildFinished {
val tempAssets = File(projectDir, "/src/main/assets-temp")
if (tempAssets.exists()) {
println("Cleaning duplicated encrypted assets, not your decrypted assets...")
val encryptedAssets = File(projectDir, "src/main/assets")
encryptedAssets.delete()

tempAssets.listFiles()?.filter{ it.isFile }?.forEach { file ->
val fis = FileInputStream(file)
val fo = File(file.absolutePath.replace("assets-temp", "assets"))
fo.parentFile.mkdirs()
val fos = FileOutputStream(fo)
val buffer = ByteArray(4096)
var n: Int
while (fis.read(buffer).also { n = it } != -1) {
fos.write(buffer, 0, n)
}
fis.close()
fos.close()
}
tempAssets.delete()
}
projectDir.cleanEncryptedAssets()
}

fun shouldEncrypt(): Boolean {
val tasks = project.gradle.startParameter.taskNames
return SHOULD_ENCRYPT_ASSETS && tasks.joinToString { it.toLowerCase() }.contains("release")
return SHOULD_ENCRYPT_ASSETS && tasks.joinToString().contains("release", ignoreCase = true)
}
37 changes: 4 additions & 33 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,56 +1,27 @@
import java.io.FileInputStream
import java.io.FileOutputStream
import Util.cleanEncryptedAssets

buildscript {
extra["kotlin_version"] = Constants.kotlinVersion
repositories {
google()
mavenCentral()
}

dependencies {
classpath("com.android.tools.build:gradle:4.2.1")
classpath("com.android.tools.build:gradle:7.1.0")
classpath(kotlin("gradle-plugin", version = Constants.kotlinVersion))
}
}

tasks {
wrapper {
gradleVersion = "7.0.2"
distributionType = Wrapper.DistributionType.ALL
}
}

allprojects {
repositories {
google()
maven("https://jitpack.io")
maven(url = "https://jitpack.io")
mavenCentral()
}
}

tasks.register<Delete>("clean") {
delete(rootProject.buildDir)
val tempAssets = File(projectDir, "/src/main/assets-temp")
if (tempAssets.exists()) {
println("cleaning encrypted assets...")
val encryptedAssets = File(projectDir, "src/main/assets")
encryptedAssets.delete()

tempAssets.listFiles()?.filter { it.isFile }?.forEach { file ->
val fis = FileInputStream(file)
val fo = File(file.absolutePath.replace("assets-temp", "assets"))
fo.parentFile.mkdirs()
val fos = FileOutputStream(fo)
val buffer = ByteArray(4096)
var n: Int
while (fis.read(buffer).also { n = it } != -1) {
fos.write(buffer, 0, n)
}
fis.close()
fos.close()
}
tempAssets.delete()
}
projectDir.cleanEncryptedAssets()
}

2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Constants.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
object Constants {

const val kotlinVersion = "1.5.0"
const val kotlinVersion = "1.6.10"

}
Loading