diff --git a/build.gradle.kts b/build.gradle.kts index b9537d6..4136d7a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -75,4 +75,4 @@ publishing { } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/library/ApkSigner.kt b/src/main/kotlin/app/revanced/library/ApkSigner.kt index d01bc73..3908422 100644 --- a/src/main/kotlin/app/revanced/library/ApkSigner.kt +++ b/src/main/kotlin/app/revanced/library/ApkSigner.kt @@ -26,8 +26,9 @@ object ApkSigner { private val logger = Logger.getLogger(app.revanced.library.ApkSigner::class.java.name) init { - if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { Security.addProvider(BouncyCastleProvider()) + } } /** @@ -39,14 +40,15 @@ object ApkSigner { */ fun newPrivateKeyCertificatePair( commonName: String = "ReVanced", - validUntil: Date = Date(System.currentTimeMillis() + 356.days.inWholeMilliseconds * 24) + 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(4096) - }.generateKeyPair() + val keyPair = + KeyPairGenerator.getInstance("RSA").apply { + initialize(4096) + }.generateKeyPair() var serialNumber: BigInteger do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) @@ -55,22 +57,22 @@ object ApkSigner { 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)) - ) + 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. * @@ -87,16 +89,18 @@ object ApkSigner { ): PrivateKeyCertificatePair { logger.fine("Reading key and certificate pair from keystore entry $keyStoreEntryAlias") - if (!keyStore.containsAlias(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 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 @@ -110,9 +114,7 @@ object ApkSigner { * @return The created keystore. * @see KeyStoreEntry */ - fun newKeyStore( - entries: List - ): KeyStore { + fun newKeyStore(entries: List): KeyStore { logger.fine("Creating keystore") return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply { @@ -124,7 +126,7 @@ object ApkSigner { entry.alias, entry.privateKeyCertificatePair.privateKey, entry.password.toCharArray(), - arrayOf(entry.privateKeyCertificatePair.certificate) + arrayOf(entry.privateKeyCertificatePair.certificate), ) } } @@ -140,10 +142,10 @@ object ApkSigner { fun newKeyStore( keyStoreOutputStream: OutputStream, keyStorePassword: String, - entries: List + entries: List, ) = newKeyStore(entries).store( keyStoreOutputStream, - keyStorePassword.toCharArray() + keyStorePassword.toCharArray(), ) // Save the keystore. /** @@ -156,7 +158,7 @@ object ApkSigner { */ fun readKeyStore( keyStoreInputStream: InputStream, - keyStorePassword: String? + keyStorePassword: String?, ): KeyStore { logger.fine("Reading keystore") @@ -164,10 +166,11 @@ object ApkSigner { try { load(keyStoreInputStream, keyStorePassword?.toCharArray()) } catch (exception: IOException) { - if (exception.cause is UnrecoverableKeyException) + if (exception.cause is UnrecoverableKeyException) { throw IllegalArgumentException("Invalid keystore password") - else + } else { throw exception + } } } } @@ -183,20 +186,21 @@ object ApkSigner { fun newApkSignerBuilder( privateKeyCertificatePair: PrivateKeyCertificatePair, signer: String, - createdBy: 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" + "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() + val signerConfig = + ApkSigner.SignerConfig.Builder( + signer, + privateKeyCertificatePair.privateKey, + listOf(privateKeyCertificatePair.certificate), + ).build() // Create the signer. return ApkSigner.Builder(listOf(signerConfig)).apply { @@ -227,10 +231,13 @@ object ApkSigner { ) = newApkSignerBuilder( readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword), signer, - createdBy + createdBy, ) - fun ApkSigner.Builder.signApk(input: File, output: File) { + fun ApkSigner.Builder.signApk( + input: File, + output: File, + ) { logger.info("Signing ${input.name}") setInputApk(input) @@ -250,7 +257,7 @@ object ApkSigner { class KeyStoreEntry( val alias: String, val password: String, - val privateKeyCertificatePair: PrivateKeyCertificatePair = newPrivateKeyCertificatePair() + val privateKeyCertificatePair: PrivateKeyCertificatePair = newPrivateKeyCertificatePair(), ) /** @@ -263,4 +270,4 @@ object ApkSigner { val privateKey: PrivateKey, val certificate: X509Certificate, ) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/library/ApkUtils.kt b/src/main/kotlin/app/revanced/library/ApkUtils.kt index f208d17..542d2db 100644 --- a/src/main/kotlin/app/revanced/library/ApkUtils.kt +++ b/src/main/kotlin/app/revanced/library/ApkUtils.kt @@ -22,7 +22,11 @@ object ApkUtils { * @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) { + fun copyAligned( + apkFile: File, + outputFile: File, + patchedEntriesSource: PatcherResult, + ) { logger.info("Aligning ${apkFile.name}") outputFile.toPath().deleteIfExists() @@ -30,13 +34,15 @@ object ApkUtils { ZipFile(outputFile).use { file -> patchedEntriesSource.dexFiles.forEach { file.addEntryCompressData( - ZipEntry(it.name), it.stream.readBytes() + ZipEntry(it.name), + it.stream.readBytes(), ) } patchedEntriesSource.resourceFile?.let { file.copyEntriesFromFileAligned( - ZipFile(it), ZipFile.apkZipEntryAlignment + ZipFile(it), + ZipFile.apkZipEntryAlignment, ) } @@ -44,7 +50,8 @@ object ApkUtils { // TODO: Fix copying resources that are not needed anymore. file.copyEntriesFromFileAligned( - ZipFile(apkFile), ZipFile.apkZipEntryAlignment + ZipFile(apkFile), + ZipFile.apkZipEntryAlignment, ) } } @@ -62,26 +69,27 @@ object ApkUtils { 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) + 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() - ) + // 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 + signingOptions.signer, ).signApk(apk, output) } @@ -101,4 +109,4 @@ object ApkUtils { val password: String = "", val signer: String = "ReVanced", ) -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/library/Options.kt b/src/main/kotlin/app/revanced/library/Options.kt index d6fe351..75b1f0e 100644 --- a/src/main/kotlin/app/revanced/library/Options.kt +++ b/src/main/kotlin/app/revanced/library/Options.kt @@ -2,7 +2,6 @@ package app.revanced.library - import app.revanced.library.Options.Patch.Option import app.revanced.patcher.PatchClass import app.revanced.patcher.PatchSet @@ -25,31 +24,37 @@ object Options { * @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 -> - val optionValue = try { - option.value - } catch (e: PatchOptionException) { - logger.warning("Using default option value for the ${patch.name} patch: ${e.message}") - option.default - } + fun serialize( + patches: PatchSet, + prettyPrint: Boolean = false, + ): String = + patches + .filter { it.options.any() } + .map { patch -> + Patch( + patch.name!!, + patch.options.values.map { option -> + val optionValue = + try { + option.value + } catch (e: PatchOptionException) { + logger.warning("Using default option value for the ${patch.name} patch: ${e.message}") + option.default + } - Option(option.key, optionValue) + Option(option.key, optionValue) + }, + ) + } + // 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) } - ) - } - // 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. @@ -70,9 +75,10 @@ object Options { filter { it.options.any() }.let { patches -> if (patches.isEmpty()) return - val jsonPatches = deserialize(json).associate { - it.patchName to it.options.associate { option -> option.key to option.value } - } + val jsonPatches = + deserialize(json).associate { + it.patchName to it.options.associate { option -> option.key to option.value } + } patches.forEach { patch -> jsonPatches[patch.name]?.let { jsonPatchOptions -> @@ -104,9 +110,8 @@ object Options { */ class Patch internal constructor( val patchName: String, - val options: List