Embed keys into dex files

This commit is contained in:
topjohnwu 2020-12-26 21:33:30 -08:00
parent 45cdb3fdb0
commit f983bfc883
13 changed files with 110 additions and 70 deletions

View File

@ -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"))))

View File

@ -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<String>) {
BootSigner.main(args)
SignBoot.main(args)
}
class b : MainActivity()

View File

@ -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")

View File

@ -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()))
}
}

View File

@ -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 <actions> [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"
);
}
}
}

View File

@ -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 <actions> [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"
);
}
}
}

View File

@ -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)