diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e29f6a92f..7024c0746 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import java.io.PrintStream + plugins { id("com.android.application") kotlin("android") @@ -63,12 +65,46 @@ android { } } -val copyUtils = tasks.register("copyUtils", Copy::class) { +tasks["preBuild"]?.dependsOn(tasks.register("copyUtils", Copy::class) { from(rootProject.file("scripts/util_functions.sh")) into("src/main/res/raw") -} +}) -tasks["preBuild"]?.dependsOn(copyUtils) +android.applicationVariants.all { + val keysDir = rootProject.file("tools/keys") + val outSrcDir = File(buildDir, "generated/source/keydata/$name") + val outSrc = File(outSrcDir, "com/topjohnwu/signing/KeyData.java") + + fun PrintStream.newField(name: String, file: File) { + println("public static byte[] $name() {") + print("byte[] buf = {") + val bytes = file.readBytes() + print(bytes.joinToString(",") { "(byte)(${it.toInt() and 0xff})" }) + println("};") + println("return buf;") + println("}") + } + + val genSrcTask = tasks.register("generate${name.capitalize()}KeyData") { + inputs.dir(keysDir) + outputs.file(outSrc) + doLast { + outSrc.parentFile.mkdirs() + PrintStream(outSrc).use { + it.println("package com.topjohnwu.signing;") + it.println("public final class KeyData {") + + it.newField("testCert", File(keysDir, "testkey.x509.pem")) + it.newField("testKey", File(keysDir, "testkey.pk8")) + it.newField("verityCert", File(keysDir, "verity.x509.pem")) + it.newField("verityKey", File(keysDir, "verity.pk8")) + + it.println("}") + } + } + } + registerJavaGeneratingTask(genSrcTask.get(), outSrcDir) +} dependencies { implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) diff --git a/app/src/main/java/a/stubs.kt b/app/src/main/java/a/stubs.kt index 5a85aabb0..7085535b7 100644 --- a/app/src/main/java/a/stubs.kt +++ b/app/src/main/java/a/stubs.kt @@ -8,10 +8,10 @@ import com.topjohnwu.magisk.core.SplashActivity import com.topjohnwu.magisk.core.download.DownloadService import com.topjohnwu.magisk.ui.MainActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity -import com.topjohnwu.signing.BootSigner +import com.topjohnwu.signing.SignBoot fun main(args: Array) { - BootSigner.main(args) + SignBoot.main(args) } class b : MainActivity() diff --git a/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt b/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt index 45fde3579..e61e529ca 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt @@ -345,7 +345,7 @@ abstract class MagiskInstallImpl : KoinComponent { val signed = File(installDir, "signed.img") try { withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) { - input, out -> SignBoot.doSignature("/boot", input, out, null, null) + input, out -> SignBoot.doSignature(null, null, input, out, "/boot") } } catch (e: IOException) { console.add("! Unable to sign image") diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt index cdf432cc9..3bde7161b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/Keygen.kt @@ -7,10 +7,12 @@ import android.util.Base64OutputStream import com.topjohnwu.magisk.core.Config import com.topjohnwu.signing.CryptoUtils.readCertificate import com.topjohnwu.signing.CryptoUtils.readPrivateKey +import com.topjohnwu.signing.KeyData import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder +import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.math.BigInteger import java.security.KeyPairGenerator @@ -58,10 +60,10 @@ class Keygen(context: Context) : CertKeyProvider { class TestProvider : CertKeyProvider { override val cert by lazy { - readCertificate(javaClass.getResourceAsStream("/keys/testkey.x509.pem")) + readCertificate(ByteArrayInputStream(KeyData.testCert())) } override val key by lazy { - readPrivateKey(javaClass.getResourceAsStream("/keys/testkey.pk8")) + readPrivateKey(ByteArrayInputStream(KeyData.testKey())) } } diff --git a/app/src/main/java/com/topjohnwu/signing/BootSigner.java b/app/src/main/java/com/topjohnwu/signing/BootSigner.java deleted file mode 100644 index 9e82bc70d..000000000 --- a/app/src/main/java/com/topjohnwu/signing/BootSigner.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.topjohnwu.signing; - -import java.io.FileInputStream; -import java.io.InputStream; - -public class BootSigner { - - public static void main(String[] args) throws Exception { - if (args.length > 0 && "-verify".equals(args[0])) { - String certPath = ""; - if (args.length >= 2) { - /* args[1] is the path to a public key certificate */ - certPath = args[1]; - } - boolean signed = SignBoot.verifySignature(System.in, - certPath.isEmpty() ? null : new FileInputStream(certPath)); - System.exit(signed ? 0 : 1); - } else if (args.length > 0 && "-sign".equals(args[0])) { - InputStream cert = null; - InputStream key = null; - String name = "/boot"; - - if (args.length >= 3) { - cert = new FileInputStream(args[1]); - key = new FileInputStream(args[2]); - } - if (args.length == 2) { - name = args[1]; - } else if (args.length >= 4) { - name = args[3]; - } - - boolean success = SignBoot.doSignature(name, System.in, System.out, cert, key); - System.exit(success ? 0 : 1); - } else { - System.err.println( - "BootSigner [args]\n" + - "Input from stdin, outputs to stdout\n" + - "\n" + - "Actions:\n" + - " -verify [x509.pem]\n" + - " verify image, cert is optional\n" + - " -sign [x509.pem] [pk8] [name]\n" + - " sign image, name, cert and key pair are optional\n" + - " name should be /boot (default) or /recovery\n" - ); - } - } -} diff --git a/app/src/main/java/com/topjohnwu/signing/SignBoot.java b/app/src/main/java/com/topjohnwu/signing/SignBoot.java index 3936f8cf3..6f80a1fb5 100644 --- a/app/src/main/java/com/topjohnwu/signing/SignBoot.java +++ b/app/src/main/java/com/topjohnwu/signing/SignBoot.java @@ -1,5 +1,8 @@ package com.topjohnwu.signing; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1InputStream; @@ -14,6 +17,7 @@ import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import java.io.ByteArrayInputStream; +import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -84,8 +88,10 @@ public class SignBoot { } } - public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut, - InputStream cert, InputStream key) { + public static boolean doSignature( + @Nullable X509Certificate cert, @Nullable PrivateKey key, + @NonNull InputStream imgIn, @NonNull OutputStream imgOut, @NonNull String target + ) { try { PushBackRWStream in = new PushBackRWStream(imgIn, imgOut); byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM]; @@ -96,16 +102,16 @@ public class SignBoot { in.unread(hdr); BootSignature bootsig = new BootSignature(target, signableSize); if (cert == null) { - cert = SignBoot.class.getResourceAsStream("/keys/verity.x509.pem"); + cert = CryptoUtils.readCertificate( + new ByteArrayInputStream(KeyData.verityCert())); } - X509Certificate certificate = CryptoUtils.readCertificate(cert); - bootsig.setCertificate(certificate); + bootsig.setCertificate(cert); if (key == null) { - key = SignBoot.class.getResourceAsStream("/keys/verity.pk8"); + key = CryptoUtils.readPrivateKey( + new ByteArrayInputStream(KeyData.verityKey())); } - PrivateKey privateKey = CryptoUtils.readPrivateKey(key); - byte[] sig = bootsig.sign(privateKey, in, signableSize); - bootsig.setSignature(sig, CryptoUtils.getSignatureAlgorithmIdentifier(privateKey)); + byte[] sig = bootsig.sign(key, in, signableSize); + bootsig.setSignature(sig, CryptoUtils.getSignatureAlgorithmIdentifier(key)); byte[] encoded_bootsig = bootsig.getEncoded(); imgOut.write(encoded_bootsig); imgOut.flush(); @@ -116,7 +122,7 @@ public class SignBoot { } } - public static boolean verifySignature(InputStream imgIn, InputStream certIn) { + public static boolean verifySignature(InputStream imgIn, X509Certificate cert) { try { // Read the header for size byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM]; @@ -142,8 +148,8 @@ public class SignBoot { } BootSignature bootsig = new BootSignature(signature); - if (certIn != null) { - bootsig.setCertificate(CryptoUtils.readCertificate(certIn)); + if (cert != null) { + bootsig.setCertificate(cert); } if (bootsig.verify(rawImg, signableSize)) { System.err.println("Signature is VALID"); @@ -314,4 +320,46 @@ public class SignBoot { } } + + public static void main(String[] args) throws Exception { + if (args.length > 0 && "-verify".equals(args[0])) { + X509Certificate cert = null; + if (args.length >= 2) { + // args[1] is the path to a public key certificate + cert = CryptoUtils.readCertificate(new FileInputStream(args[1])); + } + boolean signed = SignBoot.verifySignature(System.in, cert); + System.exit(signed ? 0 : 1); + } else if (args.length > 0 && "-sign".equals(args[0])) { + X509Certificate cert = null; + PrivateKey key = null; + String name = "/boot"; + + if (args.length >= 3) { + cert = CryptoUtils.readCertificate(new FileInputStream(args[1])); + key = CryptoUtils.readPrivateKey(new FileInputStream(args[2])); + } + if (args.length == 2) { + name = args[1]; + } else if (args.length >= 4) { + name = args[3]; + } + + boolean result = SignBoot.doSignature(cert, key, System.in, System.out, name); + System.exit(result ? 0 : 1); + } else { + System.err.println( + "BootSigner [args]\n" + + "Input from stdin, output to stdout\n" + + "\n" + + "Actions:\n" + + " -verify [x509.pem]\n" + + " verify image. cert is optional.\n" + + " -sign [x509.pem] [pk8] [name]\n" + + " sign image. name and the cert/key pair are optional.\n" + + " name should be either /boot (default) or /recovery.\n" + ); + } + } + } diff --git a/build.py b/build.py index 0cb09da71..e35c74a84 100755 --- a/build.py +++ b/build.py @@ -473,7 +473,10 @@ def zip_main(args): # chromeos for tool in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']: - source = op.join('tools', tool) + if tool == 'futility': + source = op.join('tools', tool) + else: + source = op.join('tools', 'keys', tool) target = op.join('chromeos', tool) zip_with_msg(zipf, source, target) diff --git a/tools/kernel.keyblock b/tools/keys/kernel.keyblock similarity index 100% rename from tools/kernel.keyblock rename to tools/keys/kernel.keyblock diff --git a/tools/kernel_data_key.vbprivk b/tools/keys/kernel_data_key.vbprivk similarity index 100% rename from tools/kernel_data_key.vbprivk rename to tools/keys/kernel_data_key.vbprivk diff --git a/app/src/main/resources/keys/testkey.pk8 b/tools/keys/testkey.pk8 similarity index 100% rename from app/src/main/resources/keys/testkey.pk8 rename to tools/keys/testkey.pk8 diff --git a/app/src/main/resources/keys/testkey.x509.pem b/tools/keys/testkey.x509.pem similarity index 100% rename from app/src/main/resources/keys/testkey.x509.pem rename to tools/keys/testkey.x509.pem diff --git a/app/src/main/resources/keys/verity.pk8 b/tools/keys/verity.pk8 similarity index 100% rename from app/src/main/resources/keys/verity.pk8 rename to tools/keys/verity.pk8 diff --git a/app/src/main/resources/keys/verity.x509.pem b/tools/keys/verity.x509.pem similarity index 100% rename from app/src/main/resources/keys/verity.x509.pem rename to tools/keys/verity.x509.pem