diff --git a/.releaserc b/.releaserc index a62c773..85c3f70 100644 --- a/.releaserc +++ b/.releaserc @@ -31,7 +31,7 @@ { "assets": [ { - "path": "revanced-cli/build/libs/*all.jar" + "path": "build/libs/*all.jar" } ], successComment: false diff --git a/build.gradle.kts b/build.gradle.kts index f03e3dd..f6de7ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,63 @@ plugins { - kotlin("jvm") version "1.9.0" apply false + kotlin("jvm") version "1.9.0" + alias(libs.plugins.shadow) } -allprojects { - group = "app.revanced" -} \ No newline at end of file +group = "app.revanced" + +dependencies { + implementation(libs.revanced.patcher) + implementation(libs.revanced.library) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.picocli) + + testImplementation(libs.kotlin.test) +} + +kotlin { jvmToolchain(11) } + +tasks { + test { + useJUnitPlatform() + testLogging { + events("PASSED", "SKIPPED", "FAILED") + } + } + + processResources { + expand("projectVersion" to project.version) + } + + shadowJar { + manifest { + attributes("Main-Class" to "app.revanced.cli.command.MainCommandKt") + } + minimize { + exclude(dependency("org.jetbrains.kotlin:.*")) + exclude(dependency("org.bouncycastle:.*")) + exclude(dependency("app.revanced:.*")) + } + } + + build { + dependsOn(shadowJar) + } + + /* + Dummy task to hack gradle-semantic-release-plugin to release this project. + + Explanation: + SemVer is a standard for versioning libraries. + For that reason the semantic-release plugin uses the "publish" task to publish libraries. + However, this subproject is not a library, and the "publish" task is not available for this subproject. + Because semantic-release is not designed to handle this case, we need to hack it. + + RE: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435 + */ + + register("publish") { + group = "publishing" + description = "Dummy task to hack gradle-semantic-release-plugin to release ReVanced CLI" + dependsOn(build) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 21640d2..d65846c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,27 +1,17 @@ [versions] shadow = "8.1.1" -apksig = "8.1.1" -bcpkix-jdk18on = "1.76" -jackson-module-kotlin = "2.14.3" -jadb = "2531a28109" -kotlin-reflect = "1.9.0" kotlin-test = "1.8.20-RC" kotlinx-coroutines-core = "1.7.3" picocli = "4.7.3" revanced-patcher = "15.0.1" -binary-compatibility-validator = "0.13.2" +revanced-library = "1.0.0" [libraries] -apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" } -bcpkix-jdk18on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bcpkix-jdk18on" } -jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" } -jadb = { module = "com.github.revanced:jadb", version.ref = "jadb" } -kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin-reflect" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" } picocli = { module = "info.picocli:picocli", version.ref = "picocli" } revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } +revanced-library = { module = "app.revanced:revanced-library", version.ref = "revanced-library" } [plugins] shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } -binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } diff --git a/revanced-cli/build.gradle.kts b/revanced-cli/build.gradle.kts deleted file mode 100644 index bf700b4..0000000 --- a/revanced-cli/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - kotlin("jvm") version "1.9.0" - alias(libs.plugins.shadow) -} - -dependencies { - implementation(project(":revanced-lib")) - implementation(libs.revanced.patcher) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.picocli) - - testImplementation(libs.kotlin.test) -} - -kotlin { jvmToolchain(11) } - -tasks { - test { - useJUnitPlatform() - testLogging { - events("PASSED", "SKIPPED", "FAILED") - } - } - - processResources { - expand("projectVersion" to project.version) - } - - shadowJar { - manifest { - attributes("Main-Class" to "app.revanced.cli.command.MainCommandKt") - } - minimize { - exclude(dependency("org.jetbrains.kotlin:.*")) - exclude(dependency("org.bouncycastle:.*")) - exclude(dependency("app.revanced:.*")) - } - } - - build { - dependsOn(shadowJar) - } -} diff --git a/revanced-cli/settings.gradle.kts b/revanced-cli/settings.gradle.kts deleted file mode 100644 index 028b7bc..0000000 --- a/revanced-cli/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "revanced-cli" \ No newline at end of file diff --git a/revanced-lib/api/revanced-lib.api b/revanced-lib/api/revanced-lib.api deleted file mode 100644 index 50b5bcd..0000000 --- a/revanced-lib/api/revanced-lib.api +++ /dev/null @@ -1,140 +0,0 @@ -public final class app/revanced/lib/ApkSigner { - public static final field INSTANCE Lapp/revanced/lib/ApkSigner; - public final fun newApkSignerBuilder (Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder; - public final fun newApkSignerBuilder (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder; - public final fun newKeyStore (Ljava/util/List;)Ljava/security/KeyStore; - public final fun newKeystore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/List;)V - public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair; - public static synthetic fun newPrivateKeyCertificatePair$default (Lapp/revanced/lib/ApkSigner;Ljava/lang/String;Ljava/util/Date;ILjava/lang/Object;)Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair; - public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair; - public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore; - public final fun signApk (Lcom/android/apksig/ApkSigner$Builder;Ljava/io/File;Ljava/io/File;)V -} - -public final class app/revanced/lib/ApkSigner$KeyStoreEntry { - public fun (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getAlias ()Ljava/lang/String; - public final fun getPassword ()Ljava/lang/String; - public final fun getPrivateKeyCertificatePair ()Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair; -} - -public final class app/revanced/lib/ApkSigner$PrivateKeyCertificatePair { - public fun (Ljava/security/PrivateKey;Ljava/security/cert/X509Certificate;)V - public final fun getCertificate ()Ljava/security/cert/X509Certificate; - public final fun getPrivateKey ()Ljava/security/PrivateKey; -} - -public final class app/revanced/lib/ApkUtils { - public static final field INSTANCE Lapp/revanced/lib/ApkUtils; - public final fun copyAligned (Ljava/io/File;Ljava/io/File;Lapp/revanced/patcher/PatcherResult;)V - public final fun sign (Ljava/io/File;Ljava/io/File;Lapp/revanced/lib/ApkUtils$SigningOptions;)V -} - -public final class app/revanced/lib/ApkUtils$SigningOptions { - public fun (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getAlias ()Ljava/lang/String; - public final fun getKeyStore ()Ljava/io/File; - public final fun getKeyStorePassword ()Ljava/lang/String; - public final fun getPassword ()Ljava/lang/String; - public final fun getSigner ()Ljava/lang/String; -} - -public final class app/revanced/lib/Options { - public static final field INSTANCE Lapp/revanced/lib/Options; - public final fun deserialize (Ljava/lang/String;)[Lapp/revanced/lib/Options$Patch; - public final fun serialize (Ljava/util/Set;Z)Ljava/lang/String; - public static synthetic fun serialize$default (Lapp/revanced/lib/Options;Ljava/util/Set;ZILjava/lang/Object;)Ljava/lang/String; - public final fun setOptions (Ljava/util/Set;Ljava/io/File;)V - public final fun setOptions (Ljava/util/Set;Ljava/lang/String;)V -} - -public final class app/revanced/lib/Options$Patch { - public final fun getOptions ()Ljava/util/List; - public final fun getPatchName ()Ljava/lang/String; -} - -public final class app/revanced/lib/Options$Patch$Option { - public final fun getKey ()Ljava/lang/String; - public final fun getValue ()Ljava/lang/Object; -} - -public final class app/revanced/lib/PatchUtils { - public static final field INSTANCE Lapp/revanced/lib/PatchUtils; - public final fun getMostCommonCompatibleVersion (Ljava/util/Set;Ljava/lang/String;)Ljava/lang/String; -} - -public abstract class app/revanced/lib/adb/AdbManager { - public static final field Companion Lapp/revanced/lib/adb/AdbManager$Companion; - public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - protected final fun getDevice ()Lse/vidstige/jadb/JadbDevice; - protected final fun getLogger ()Ljava/util/logging/Logger; - public fun install (Lapp/revanced/lib/adb/AdbManager$Apk;)V - public fun uninstall (Ljava/lang/String;)V -} - -public final class app/revanced/lib/adb/AdbManager$Apk { - public fun (Ljava/io/File;Ljava/lang/String;)V - public synthetic fun (Ljava/io/File;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getFile ()Ljava/io/File; - public final fun getPackageName ()Ljava/lang/String; -} - -public final class app/revanced/lib/adb/AdbManager$Companion { - public final fun getAdbManager (Ljava/lang/String;Z)Lapp/revanced/lib/adb/AdbManager; - public static synthetic fun getAdbManager$default (Lapp/revanced/lib/adb/AdbManager$Companion;Ljava/lang/String;ZILjava/lang/Object;)Lapp/revanced/lib/adb/AdbManager; -} - -public final class app/revanced/lib/adb/AdbManager$DeviceNotFoundException : java/lang/Exception { -} - -public final class app/revanced/lib/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception { -} - -public final class app/revanced/lib/adb/AdbManager$PackageNameRequiredException : java/lang/Exception { -} - -public final class app/revanced/lib/adb/AdbManager$RootAdbManager : app/revanced/lib/adb/AdbManager { - public static final field Utils Lapp/revanced/lib/adb/AdbManager$RootAdbManager$Utils; - public fun install (Lapp/revanced/lib/adb/AdbManager$Apk;)V - public fun uninstall (Ljava/lang/String;)V -} - -public final class app/revanced/lib/adb/AdbManager$RootAdbManager$Utils { -} - -public final class app/revanced/lib/adb/AdbManager$UserAdbManager : app/revanced/lib/adb/AdbManager { - public fun install (Lapp/revanced/lib/adb/AdbManager$Apk;)V - public fun uninstall (Ljava/lang/String;)V -} - -public final class app/revanced/lib/logging/Logger { - public static final field INSTANCE Lapp/revanced/lib/logging/Logger; - public final fun addHandler (Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V - public final fun removeAllHandlers ()V - public final fun setDefault ()V - public final fun setFormat (Ljava/lang/String;)V - public static synthetic fun setFormat$default (Lapp/revanced/lib/logging/Logger;Ljava/lang/String;ILjava/lang/Object;)V -} - -public final class app/revanced/lib/zip/ZipFile : java/io/Closeable { - public static final field ApkZipFile Lapp/revanced/lib/zip/ZipFile$ApkZipFile; - public fun (Ljava/io/File;)V - public final fun addEntryCompressData (Lapp/revanced/lib/zip/structures/ZipEntry;[B)V - public fun close ()V - public final fun copyEntriesFromFileAligned (Lapp/revanced/lib/zip/ZipFile;Lkotlin/jvm/functions/Function1;)V -} - -public final class app/revanced/lib/zip/ZipFile$ApkZipFile { - public final fun getApkZipEntryAlignment ()Lkotlin/jvm/functions/Function1; -} - -public final class app/revanced/lib/zip/structures/ZipEntry { - public static final field Companion Lapp/revanced/lib/zip/structures/ZipEntry$Companion; - public fun (Ljava/lang/String;)V -} - -public final class app/revanced/lib/zip/structures/ZipEntry$Companion { -} - diff --git a/revanced-lib/build.gradle.kts b/revanced-lib/build.gradle.kts deleted file mode 100644 index 85ddd27..0000000 --- a/revanced-lib/build.gradle.kts +++ /dev/null @@ -1,78 +0,0 @@ -plugins { - kotlin("jvm") version "1.9.0" - alias(libs.plugins.binary.compatibility.validator) - `maven-publish` -} - -dependencies { - implementation(libs.revanced.patcher) - implementation(libs.kotlin.reflect) - implementation(libs.jadb) // Updated fork - implementation(libs.apksig) - implementation(libs.bcpkix.jdk18on) - implementation(libs.jackson.module.kotlin) - - testImplementation(libs.revanced.patcher) - testImplementation(libs.kotlin.test) -} - -tasks { - test { - useJUnitPlatform() - testLogging { - events("PASSED", "SKIPPED", "FAILED") - } - } -} - -kotlin { jvmToolchain(11) } - -java { - withSourcesJar() -} - -publishing { - repositories { - mavenLocal() - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/revanced/revanced-cli") - credentials { - username = System.getenv("GITHUB_ACTOR") - password = System.getenv("GITHUB_TOKEN") - } - } - } - publications { - create("gpr") { - from(components["java"]) - - version = project.version.toString() - - pom { - name = "ReVanced Library" - description = "Library containing common utilities for ReVanced" - url = "https://revanced.app" - - licenses { - license { - name = "GNU General Public License v3.0" - url = "https://www.gnu.org/licenses/gpl-3.0.en.html" - } - } - developers { - developer { - id = "ReVanced" - name = "ReVanced" - email = "contact@revanced.app" - } - } - scm { - connection = "scm:git:git://github.com/revanced/revanced-cli.git" - developerConnection = "scm:git:git@github.com:revanced/revanced-cli.git" - url = "https://github.com/revanced/revanced-cli" - } - } - } - } -} \ No newline at end of file diff --git a/revanced-lib/settings.gradle.kts b/revanced-lib/settings.gradle.kts deleted file mode 100644 index f1bcd72..0000000 --- a/revanced-lib/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "revanced-lib" \ No newline at end of file diff --git a/revanced-lib/src/main/kotlin/app/revanced/lib/ApkSigner.kt b/revanced-lib/src/main/kotlin/app/revanced/lib/ApkSigner.kt deleted file mode 100644 index 31f4f6e..0000000 --- a/revanced-lib/src/main/kotlin/app/revanced/lib/ApkSigner.kt +++ /dev/null @@ -1,265 +0,0 @@ -package app.revanced.lib - -import com.android.apksig.ApkSigner -import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -import org.bouncycastle.cert.X509v3CertificateBuilder -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter -import org.bouncycastle.jce.provider.BouncyCastleProvider -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder -import java.io.File -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream -import java.math.BigInteger -import java.security.* -import java.security.cert.X509Certificate -import java.util.* -import java.util.logging.Logger -import kotlin.time.Duration.Companion.days - -/** - * Utility class for writing or reading keystore files and entries as well as signing APK files. - */ -@Suppress("MemberVisibilityCanBePrivate", "unused") -object ApkSigner { - private val logger = Logger.getLogger(app.revanced.lib.ApkSigner::class.java.name) - - init { - if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) - Security.addProvider(BouncyCastleProvider()) - } - - /** - * Create a new [PrivateKeyCertificatePair]. - * - * @param commonName The common name of the certificate. - * @param validUntil The date until the certificate is valid. - * @return The created [PrivateKeyCertificatePair]. - */ - fun newPrivateKeyCertificatePair( - commonName: String = "ReVanced", - validUntil: Date = Date(System.currentTimeMillis() + 356.days.inWholeMilliseconds * 24) - ): PrivateKeyCertificatePair { - logger.fine("Creating certificate for $commonName") - - // Generate a new key pair. - val keyPair = KeyPairGenerator.getInstance("RSA").apply { - initialize(2048) - }.generateKeyPair() - - var serialNumber: BigInteger - do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) - while (serialNumber < BigInteger.ZERO) - - val name = X500Name("CN=$commonName") - - // Create a new certificate. - val certificate = JcaX509CertificateConverter().getCertificate( - X509v3CertificateBuilder( - name, - serialNumber, - Date(System.currentTimeMillis()), - validUntil, - Locale.ENGLISH, - name, - SubjectPublicKeyInfo.getInstance(keyPair.public.encoded) - ).build(JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private)) - ) - - return PrivateKeyCertificatePair(keyPair.private, certificate) - } - - - /** - * Read a [PrivateKeyCertificatePair] from a keystore entry. - * - * @param keyStore The keystore to read the entry from. - * @param keyStoreEntryAlias The alias of the key store entry to read. - * @param keyStoreEntryPassword The password for recovering the signing key. - * @return The read [PrivateKeyCertificatePair]. - * @throws IllegalArgumentException If the keystore does not contain the given alias or the password is invalid. - */ - fun readKeyCertificatePair( - keyStore: KeyStore, - keyStoreEntryAlias: String, - keyStoreEntryPassword: String, - ): PrivateKeyCertificatePair { - logger.fine("Reading key and certificate pair from keystore entry $keyStoreEntryAlias") - - if (!keyStore.containsAlias(keyStoreEntryAlias)) - throw IllegalArgumentException("Keystore does not contain alias $keyStoreEntryAlias") - - // Read the private key and certificate from the keystore. - - val privateKey = try { - keyStore.getKey(keyStoreEntryAlias, keyStoreEntryPassword.toCharArray()) as PrivateKey - } catch (exception: UnrecoverableKeyException) { - throw IllegalArgumentException("Invalid password for keystore entry $keyStoreEntryAlias") - } - - val certificate = keyStore.getCertificate(keyStoreEntryAlias) as X509Certificate - - return PrivateKeyCertificatePair(privateKey, certificate) - } - - /** - * Create a new keystore with a new keypair. - * - * @param entries The entries to add to the keystore. - * @return The created keystore. - * @see KeyStoreEntry - */ - fun newKeyStore( - entries: List - ): KeyStore { - logger.fine("Creating keystore") - - return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply { - entries.forEach { entry -> - load(null) - // Add all entries to the keystore. - setKeyEntry( - entry.alias, - entry.privateKeyCertificatePair.privateKey, - entry.password.toCharArray(), - arrayOf(entry.privateKeyCertificatePair.certificate) - ) - } - } - } - - /** - * Create a new keystore with a new keypair and saves it to the given [keyStoreOutputStream]. - * - * @param keyStoreOutputStream The stream to write the keystore to. - * @param keyStorePassword The password for the keystore. - * @param entries The entries to add to the keystore. - */ - fun newKeystore( - keyStoreOutputStream: OutputStream, - keyStorePassword: String, - entries: List - ) = newKeyStore(entries).store( - keyStoreOutputStream, - keyStorePassword.toCharArray() - ) // Save the keystore. - - /** - * Read a keystore from the given [keyStoreInputStream]. - * - * @param keyStoreInputStream The stream to read the keystore from. - * @param keyStorePassword The password for the keystore. - * @return The keystore. - * @throws IllegalArgumentException If the keystore password is invalid. - */ - fun readKeyStore( - keyStoreInputStream: InputStream, - keyStorePassword: String? - ): KeyStore { - logger.fine("Reading keystore") - - return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply { - try { - load(keyStoreInputStream, keyStorePassword?.toCharArray()) - } catch (exception: IOException) { - if (exception.cause is UnrecoverableKeyException) - throw IllegalArgumentException("Invalid keystore password") - else - throw exception - } - } - } - - /** - * Create a new [ApkSigner.Builder]. - * - * @param privateKeyCertificatePair The private key and certificate pair to use for signing. - * @param signer The name of the signer. - * @param createdBy The value for the `Created-By` attribute in the APK's manifest. - * @return The created [ApkSigner.Builder] instance. - */ - fun newApkSignerBuilder( - privateKeyCertificatePair: PrivateKeyCertificatePair, - signer: String, - createdBy: String - ): ApkSigner.Builder { - logger.fine( - "Creating new ApkSigner " + - "with $signer as signer and " + - "$createdBy as Created-By attribute in the APK's manifest" - ) - - // Create the signer config. - val signerConfig = ApkSigner.SignerConfig.Builder( - signer, - privateKeyCertificatePair.privateKey, - listOf(privateKeyCertificatePair.certificate) - ).build() - - // Create the signer. - return ApkSigner.Builder(listOf(signerConfig)).apply { - setCreatedBy(createdBy) - } - } - - /** - * Create a new [ApkSigner.Builder]. - * - * @param keyStore The keystore to use for signing. - * @param keyStoreEntryAlias The alias of the key store entry to use for signing. - * @param keyStoreEntryPassword The password for recovering the signing key. - * @param signer The name of the signer. - * @param createdBy The value for the `Created-By` attribute in the APK's manifest. - * @return The created [ApkSigner.Builder] instance. - * @see KeyStoreEntry - * @see PrivateKeyCertificatePair - * @see ApkSigner.Builder.setCreatedBy - * @see ApkSigner.Builder.signApk - */ - fun newApkSignerBuilder( - keyStore: KeyStore, - keyStoreEntryAlias: String, - keyStoreEntryPassword: String, - signer: String, - createdBy: String, - ) = newApkSignerBuilder( - readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword), - signer, - createdBy - ) - - fun ApkSigner.Builder.signApk(input: File, output: File) { - logger.info("Signing ${input.name}") - - setInputApk(input) - setOutputApk(output) - - build().sign() - } - - /** - * An entry in a keystore. - * - * @param alias The alias of the entry. - * @param password The password for recovering the signing key. - * @param privateKeyCertificatePair The private key and certificate pair. - * @see PrivateKeyCertificatePair - */ - class KeyStoreEntry( - val alias: String, - val password: String, - val privateKeyCertificatePair: PrivateKeyCertificatePair = newPrivateKeyCertificatePair() - ) - - /** - * A private key and certificate pair. - * - * @param privateKey The private key. - * @param certificate The certificate. - */ - class PrivateKeyCertificatePair( - val privateKey: PrivateKey, - val certificate: X509Certificate, - ) -} \ No newline at end of file diff --git a/revanced-lib/src/main/kotlin/app/revanced/lib/ApkUtils.kt b/revanced-lib/src/main/kotlin/app/revanced/lib/ApkUtils.kt deleted file mode 100644 index de48db5..0000000 --- a/revanced-lib/src/main/kotlin/app/revanced/lib/ApkUtils.kt +++ /dev/null @@ -1,104 +0,0 @@ -package app.revanced.lib - -import app.revanced.lib.ApkSigner.signApk -import app.revanced.lib.zip.ZipFile -import app.revanced.lib.zip.structures.ZipEntry -import app.revanced.patcher.PatcherResult -import java.io.File -import java.util.logging.Logger -import kotlin.io.path.deleteIfExists - -/** - * Utility functions for working with apks. - */ -@Suppress("MemberVisibilityCanBePrivate", "unused") -object ApkUtils { - private val logger = Logger.getLogger(ApkUtils::class.java.name) - - /** - * Creates a new apk from [apkFile] and [patchedEntriesSource] and writes it to [outputFile]. - * - * @param apkFile The apk to copy entries from. - * @param outputFile The apk to write the new entries to. - * @param patchedEntriesSource The result of the patcher to add the patched dex files and resources. - */ - fun copyAligned(apkFile: File, outputFile: File, patchedEntriesSource: PatcherResult) { - logger.info("Aligning ${apkFile.name}") - - outputFile.toPath().deleteIfExists() - - ZipFile(outputFile).use { file -> - patchedEntriesSource.dexFiles.forEach { - file.addEntryCompressData( - ZipEntry(it.name), it.stream.readBytes() - ) - } - - patchedEntriesSource.resourceFile?.let { - file.copyEntriesFromFileAligned( - ZipFile(it), ZipFile.apkZipEntryAlignment - ) - } - - // TODO: Do not compress result.doNotCompress - - // TODO: Fix copying resources that are not needed anymore. - file.copyEntriesFromFileAligned( - ZipFile(apkFile), ZipFile.apkZipEntryAlignment - ) - } - } - - /** - * Signs the [apk] file and writes it to [output]. - * - * @param apk The apk to sign. - * @param output The apk to write the signed apk to. - * @param signingOptions The options to use for signing. - */ - fun sign( - apk: File, - output: File, - signingOptions: SigningOptions, - ) { - // Get the keystore from the file or create a new one. - val keyStore = if (signingOptions.keyStore.exists()) { - ApkSigner.readKeyStore(signingOptions.keyStore.inputStream(), signingOptions.keyStorePassword) - } else { - val entry = ApkSigner.KeyStoreEntry(signingOptions.alias, signingOptions.password) - - // Create a new keystore with a new keypair and saves it. - ApkSigner.newKeyStore(listOf(entry)).also { keyStore -> - keyStore.store( - signingOptions.keyStore.outputStream(), - signingOptions.keyStorePassword?.toCharArray() - ) - } - } - - ApkSigner.newApkSignerBuilder( - keyStore, - signingOptions.alias, - signingOptions.password, - signingOptions.signer, - signingOptions.signer - ).signApk(apk, output) - } - - /** - * Options for signing an apk. - * - * @param keyStore The keystore to use for signing. - * @param keyStorePassword The password for the keystore. - * @param alias The alias of the key store entry to use for signing. - * @param password The password for recovering the signing key. - * @param signer The name of the signer. - */ - class SigningOptions( - val keyStore: File, - val keyStorePassword: String?, - val alias: String = "ReVanced Key", - val password: String = "", - val signer: String = "ReVanced", - ) -} \ No newline at end of file diff --git a/revanced-lib/src/main/kotlin/app/revanced/lib/Options.kt b/revanced-lib/src/main/kotlin/app/revanced/lib/Options.kt deleted file mode 100644 index 3bef852..0000000 --- a/revanced-lib/src/main/kotlin/app/revanced/lib/Options.kt +++ /dev/null @@ -1,107 +0,0 @@ -@file:Suppress("MemberVisibilityCanBePrivate") - -package app.revanced.lib - - -import app.revanced.lib.Options.Patch.Option -import app.revanced.patcher.PatchClass -import app.revanced.patcher.PatchSet -import app.revanced.patcher.patch.options.PatchOptionException -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import java.io.File -import java.util.logging.Logger - -private typealias PatchList = List - -object Options { - private val logger = Logger.getLogger(Options::class.java.name) - - private var mapper = jacksonObjectMapper() - - /** - * Serializes the options for the patches in the list. - * - * @param patches The list of patches to serialize. - * @param prettyPrint Whether to pretty print the JSON. - * @return The JSON string containing the options. - */ - fun serialize(patches: PatchSet, prettyPrint: Boolean = false): String = patches - .filter { it.options.any() } - .map { patch -> - Patch( - patch.name!!, - patch.options.values.map { option -> Option(option.key, option.value) } - ) - } - // See https://github.com/revanced/revanced-patches/pull/2434/commits/60e550550b7641705e81aa72acfc4faaebb225e7. - .distinctBy { it.patchName } - .let { - if (prettyPrint) - mapper.writerWithDefaultPrettyPrinter().writeValueAsString(it) - else - mapper.writeValueAsString(it) - } - - /** - * Deserializes the options for the patches in the list. - * - * @param json The JSON string containing the options. - * @return The list of [Patch]s. - * @see Patch - * @see PatchList - */ - fun deserialize(json: String): Array = mapper.readValue(json, Array::class.java) - - /** - * Sets the options for the patches in the list. - * - * @param json The JSON string containing the options. - */ - fun PatchSet.setOptions(json: String) { - filter { it.options.any() }.let { patches -> - if (patches.isEmpty()) return - - val patchOptions = deserialize(json) - - patches.forEach patch@{ patch -> - patchOptions.find { option -> option.patchName == patch.name!! }?.let { - it.options.forEach { option -> - try { - patch.options[option.key] = option.value - } catch (e: PatchOptionException) { - logger.severe(e.toString()) - } - } - } - } - } - } - - /** - * Sets the options for the patches in the list. - * - * @param file The file containing the JSON string containing the options. - * @see setOptions - */ - fun PatchSet.setOptions(file: File) = setOptions(file.readText()) - - /** - * Data class for a patch and its [Option]s. - * - * @property patchName The name of the patch. - * @property options The [Option]s for the patch. - */ - class Patch internal constructor( - val patchName: String, - val options: List