mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57:39 +00:00
Remove unused code and logic
This commit is contained in:
parent
af65d07456
commit
f33343b4e6
@ -20,13 +20,11 @@ import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
||||
import com.topjohnwu.magisk.core.ktx.reboot
|
||||
import com.topjohnwu.magisk.core.ktx.toast
|
||||
import com.topjohnwu.magisk.core.ktx.withStreams
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.signing.SignBoot
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.internal.NOPList
|
||||
@ -481,22 +479,6 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
}
|
||||
|
||||
private fun patchBoot(): Boolean {
|
||||
var isSigned = false
|
||||
if (!srcBoot.isCharacter) {
|
||||
try {
|
||||
srcBoot.newInputStream().use {
|
||||
if (SignBoot.verifySignature(it, null)) {
|
||||
isSigned = true
|
||||
console.add("- Boot image is signed with AVB 1.0")
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unable to check signature")
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val newBoot = installDir.getChildFile("new-boot.img")
|
||||
if (!useRootDir) {
|
||||
// Create output files before hand
|
||||
@ -511,31 +493,11 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
"PATCHVBMETAFLAG=${Config.patchVbmeta} " +
|
||||
"RECOVERYMODE=${Config.recovery} " +
|
||||
"SYSTEM_ROOT=${Info.isSAR} " +
|
||||
"sh boot_patch.sh $srcBoot")
|
||||
"sh boot_patch.sh $srcBoot",
|
||||
"./magiskboot cleanup",
|
||||
"cd /")
|
||||
|
||||
if (!cmds.sh().isSuccess)
|
||||
return false
|
||||
|
||||
val job = shell.newJob().add("./magiskboot cleanup", "cd /")
|
||||
|
||||
if (isSigned) {
|
||||
console.add("- Signing boot image with verity keys")
|
||||
val signed = File.createTempFile("signed", ".img", context.cacheDir)
|
||||
try {
|
||||
val src = newBoot.newInputStream().buffered()
|
||||
val out = signed.outputStream().buffered()
|
||||
withStreams(src, out) { _, _ ->
|
||||
SignBoot.doSignature(null, null, src, out, "/boot")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unable to sign image")
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
job.add("cat $signed > $newBoot", "rm -f $signed")
|
||||
}
|
||||
job.exec()
|
||||
return true
|
||||
return cmds.sh().isSuccess
|
||||
}
|
||||
|
||||
private fun flashBoot() = "direct_install $installDir $srcBoot".sh().isSuccess
|
||||
|
@ -1,115 +0,0 @@
|
||||
package com.topjohnwu.magisk.signing;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1InputStream;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.ECPrivateKeySpec;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CryptoUtils {
|
||||
|
||||
static final Map<String, String> ID_TO_ALG;
|
||||
static final Map<String, String> ALG_TO_ID;
|
||||
|
||||
static {
|
||||
ID_TO_ALG = new HashMap<>();
|
||||
ALG_TO_ID = new HashMap<>();
|
||||
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA256.getId(), "SHA256withECDSA");
|
||||
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA384.getId(), "SHA384withECDSA");
|
||||
ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA512.getId(), "SHA512withECDSA");
|
||||
ID_TO_ALG.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1withRSA");
|
||||
ID_TO_ALG.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256withRSA");
|
||||
ID_TO_ALG.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512withRSA");
|
||||
ALG_TO_ID.put("SHA256withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256.getId());
|
||||
ALG_TO_ID.put("SHA384withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384.getId());
|
||||
ALG_TO_ID.put("SHA512withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512.getId());
|
||||
ALG_TO_ID.put("SHA1withRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption.getId());
|
||||
ALG_TO_ID.put("SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption.getId());
|
||||
ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId());
|
||||
}
|
||||
|
||||
static String getSignatureAlgorithm(Key key) throws Exception {
|
||||
if ("EC".equals(key.getAlgorithm())) {
|
||||
int curveSize;
|
||||
KeyFactory factory = KeyFactory.getInstance("EC");
|
||||
if (key instanceof PublicKey) {
|
||||
ECPublicKeySpec spec = factory.getKeySpec(key, ECPublicKeySpec.class);
|
||||
curveSize = spec.getParams().getCurve().getField().getFieldSize();
|
||||
} else if (key instanceof PrivateKey) {
|
||||
ECPrivateKeySpec spec = factory.getKeySpec(key, ECPrivateKeySpec.class);
|
||||
curveSize = spec.getParams().getCurve().getField().getFieldSize();
|
||||
} else {
|
||||
throw new InvalidKeySpecException();
|
||||
}
|
||||
if (curveSize <= 256) {
|
||||
return "SHA256withECDSA";
|
||||
} else if (curveSize <= 384) {
|
||||
return "SHA384withECDSA";
|
||||
} else {
|
||||
return "SHA512withECDSA";
|
||||
}
|
||||
} else if ("RSA".equals(key.getAlgorithm())) {
|
||||
return "SHA256withRSA";
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm());
|
||||
}
|
||||
}
|
||||
|
||||
static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) throws Exception {
|
||||
String id = ALG_TO_ID.get(getSignatureAlgorithm(key));
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm());
|
||||
}
|
||||
return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id));
|
||||
}
|
||||
|
||||
public static X509Certificate readCertificate(InputStream input)
|
||||
throws IOException, GeneralSecurityException {
|
||||
try {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate) cf.generateCertificate(input);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a PKCS#8 format private key. */
|
||||
public static PrivateKey readPrivateKey(InputStream input)
|
||||
throws IOException, GeneralSecurityException {
|
||||
try {
|
||||
ByteArrayStream buf = new ByteArrayStream();
|
||||
buf.readFrom(input);
|
||||
byte[] bytes = buf.toByteArray();
|
||||
/* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
|
||||
/*
|
||||
* Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
|
||||
* OID and use that to construct a KeyFactory.
|
||||
*/
|
||||
ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
|
||||
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
|
||||
String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
|
||||
return KeyFactory.getInstance(algOid).generatePrivate(spec);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,382 +0,0 @@
|
||||
package com.topjohnwu.magisk.signing;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1Encodable;
|
||||
import org.bouncycastle.asn1.ASN1EncodableVector;
|
||||
import org.bouncycastle.asn1.ASN1InputStream;
|
||||
import org.bouncycastle.asn1.ASN1Integer;
|
||||
import org.bouncycastle.asn1.ASN1Object;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.ASN1Primitive;
|
||||
import org.bouncycastle.asn1.ASN1Sequence;
|
||||
import org.bouncycastle.asn1.DEROctetString;
|
||||
import org.bouncycastle.asn1.DERPrintableString;
|
||||
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;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SignBoot {
|
||||
|
||||
private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632;
|
||||
private static final int BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET = 1648;
|
||||
|
||||
// Arbitrary maximum header version value; when greater assume the field is dt/extra size
|
||||
private static final int BOOT_IMAGE_HEADER_VERSION_MAXIMUM = 8;
|
||||
|
||||
// Maximum header size byte value to read (currently the bootimg minimum page size)
|
||||
private static final int BOOT_IMAGE_HEADER_SIZE_MAXIMUM = 2048;
|
||||
|
||||
private static class PushBackRWStream extends FilterInputStream {
|
||||
private OutputStream out;
|
||||
private int pos = 0;
|
||||
private byte[] backBuf;
|
||||
|
||||
PushBackRWStream(InputStream in, OutputStream o) {
|
||||
super(in);
|
||||
out = o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int b;
|
||||
if (backBuf != null && backBuf.length > pos) {
|
||||
b = backBuf[pos++];
|
||||
} else {
|
||||
b = super.read();
|
||||
out.write(b);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] bytes, int off, int len) throws IOException {
|
||||
int read = 0;
|
||||
if (backBuf != null && backBuf.length > pos) {
|
||||
read = Math.min(len, backBuf.length - pos);
|
||||
System.arraycopy(backBuf, pos, bytes, off, read);
|
||||
pos += read;
|
||||
off += read;
|
||||
len -= read;
|
||||
}
|
||||
if (len > 0) {
|
||||
int ar = super.read(bytes, off, len);
|
||||
read += ar;
|
||||
out.write(bytes, off, ar);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
void unread(byte[] buf) {
|
||||
backBuf = buf;
|
||||
}
|
||||
}
|
||||
|
||||
private static int fullRead(InputStream in, byte[] b) throws IOException {
|
||||
return fullRead(in, b, 0, b.length);
|
||||
}
|
||||
|
||||
private static int fullRead(InputStream in, byte[] b, int off, int len) throws IOException {
|
||||
int n = 0;
|
||||
while (n < len) {
|
||||
int count = in.read(b, off + n, len - n);
|
||||
if (count <= 0)
|
||||
break;
|
||||
n += count;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
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];
|
||||
// First read the header
|
||||
fullRead(in, hdr);
|
||||
int signableSize = getSignableImageSize(hdr);
|
||||
// Unread header
|
||||
in.unread(hdr);
|
||||
BootSignature bootsig = new BootSignature(target, signableSize);
|
||||
if (cert == null) {
|
||||
cert = CryptoUtils.readCertificate(
|
||||
new ByteArrayInputStream(KeyData.verityCert()));
|
||||
}
|
||||
bootsig.setCertificate(cert);
|
||||
if (key == null) {
|
||||
key = CryptoUtils.readPrivateKey(
|
||||
new ByteArrayInputStream(KeyData.verityKey()));
|
||||
}
|
||||
byte[] sig = bootsig.sign(key, in, signableSize);
|
||||
bootsig.setSignature(sig, CryptoUtils.getSignatureAlgorithmIdentifier(key));
|
||||
byte[] encoded_bootsig = bootsig.getEncoded();
|
||||
imgOut.write(encoded_bootsig);
|
||||
imgOut.flush();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean verifySignature(InputStream imgIn, X509Certificate cert) {
|
||||
try {
|
||||
// Read the header for size
|
||||
byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
|
||||
if (fullRead(imgIn, hdr) != hdr.length) {
|
||||
System.err.println("Unable to read image header");
|
||||
return false;
|
||||
}
|
||||
int signableSize = getSignableImageSize(hdr);
|
||||
|
||||
// Read the rest of the image
|
||||
byte[] rawImg = Arrays.copyOf(hdr, signableSize);
|
||||
int remain = signableSize - hdr.length;
|
||||
if (fullRead(imgIn, rawImg, hdr.length, remain) != remain) {
|
||||
System.err.println("Unable to read image");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read footer, which contains the signature
|
||||
byte[] signature = new byte[4096];
|
||||
if (imgIn.read(signature) == -1 || Arrays.equals(signature, new byte [signature.length])) {
|
||||
System.err.println("Invalid image: not signed");
|
||||
return false;
|
||||
}
|
||||
|
||||
BootSignature bootsig = new BootSignature(signature);
|
||||
if (cert != null) {
|
||||
bootsig.setCertificate(cert);
|
||||
}
|
||||
if (bootsig.verify(rawImg, signableSize)) {
|
||||
System.err.println("Signature is VALID");
|
||||
return true;
|
||||
} else {
|
||||
System.err.println("Signature is INVALID");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getSignableImageSize(byte[] data) throws Exception {
|
||||
if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8),
|
||||
"ANDROID!".getBytes("US-ASCII"))) {
|
||||
throw new IllegalArgumentException("Invalid image header: missing magic");
|
||||
}
|
||||
ByteBuffer image = ByteBuffer.wrap(data);
|
||||
image.order(ByteOrder.LITTLE_ENDIAN);
|
||||
image.getLong(); // magic
|
||||
int kernelSize = image.getInt();
|
||||
image.getInt(); // kernel_addr
|
||||
int ramdskSize = image.getInt();
|
||||
image.getInt(); // ramdisk_addr
|
||||
int secondSize = image.getInt();
|
||||
image.getLong(); // second_addr + tags_addr
|
||||
int pageSize = image.getInt();
|
||||
if (pageSize >= 0x02000000) {
|
||||
throw new IllegalArgumentException("Invalid image header: PXA header detected");
|
||||
}
|
||||
int length = pageSize // include the page aligned image header
|
||||
+ ((kernelSize + pageSize - 1) / pageSize) * pageSize
|
||||
+ ((ramdskSize + pageSize - 1) / pageSize) * pageSize
|
||||
+ ((secondSize + pageSize - 1) / pageSize) * pageSize;
|
||||
int headerVersion = image.getInt(); // boot image header version or dt/extra size
|
||||
if (headerVersion > 0 && headerVersion < BOOT_IMAGE_HEADER_VERSION_MAXIMUM) {
|
||||
image.position(BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET);
|
||||
int recoveryDtboLength = image.getInt();
|
||||
length += ((recoveryDtboLength + pageSize - 1) / pageSize) * pageSize;
|
||||
image.getLong(); // recovery_dtbo address
|
||||
int headerSize = image.getInt();
|
||||
if (headerVersion == 2) {
|
||||
image.position(BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET);
|
||||
int dtbLength = image.getInt();
|
||||
length += ((dtbLength + pageSize - 1) / pageSize) * pageSize;
|
||||
image.getLong(); // dtb address
|
||||
}
|
||||
if (image.position() != headerSize) {
|
||||
throw new IllegalArgumentException("Invalid image header: invalid header length");
|
||||
}
|
||||
} else {
|
||||
// headerVersion is 0 or actually dt/extra size in this case
|
||||
length += ((headerVersion + pageSize - 1) / pageSize) * pageSize;
|
||||
}
|
||||
length = ((length + pageSize - 1) / pageSize) * pageSize;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Invalid image header: invalid length");
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static class BootSignature extends ASN1Object {
|
||||
private ASN1Integer formatVersion;
|
||||
private ASN1Encodable certificate;
|
||||
private AlgorithmIdentifier algId;
|
||||
private DERPrintableString target;
|
||||
private ASN1Integer length;
|
||||
private DEROctetString signature;
|
||||
private PublicKey publicKey;
|
||||
private static final int FORMAT_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Initializes the object for signing an image file
|
||||
* @param target Target name, included in the signed data
|
||||
* @param length Length of the image, included in the signed data
|
||||
*/
|
||||
public BootSignature(String target, int length) {
|
||||
this.formatVersion = new ASN1Integer(FORMAT_VERSION);
|
||||
this.target = new DERPrintableString(target);
|
||||
this.length = new ASN1Integer(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the object for verifying a signed image file
|
||||
* @param signature Signature footer
|
||||
*/
|
||||
public BootSignature(byte[] signature) throws Exception {
|
||||
ASN1InputStream stream = new ASN1InputStream(signature);
|
||||
ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
|
||||
formatVersion = (ASN1Integer) sequence.getObjectAt(0);
|
||||
if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
|
||||
throw new IllegalArgumentException("Unsupported format version");
|
||||
}
|
||||
certificate = sequence.getObjectAt(1);
|
||||
byte[] encoded = ((ASN1Object) certificate).getEncoded();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
|
||||
publicKey = c.getPublicKey();
|
||||
ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
|
||||
this.algId = new AlgorithmIdentifier((ASN1ObjectIdentifier) algId.getObjectAt(0));
|
||||
ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
|
||||
target = (DERPrintableString) attrs.getObjectAt(0);
|
||||
length = (ASN1Integer) attrs.getObjectAt(1);
|
||||
this.signature = (DEROctetString) sequence.getObjectAt(4);
|
||||
}
|
||||
|
||||
public ASN1Object getAuthenticatedAttributes() {
|
||||
ASN1EncodableVector attrs = new ASN1EncodableVector();
|
||||
attrs.add(target);
|
||||
attrs.add(length);
|
||||
return new DERSequence(attrs);
|
||||
}
|
||||
|
||||
public byte[] getEncodedAuthenticatedAttributes() throws IOException {
|
||||
return getAuthenticatedAttributes().getEncoded();
|
||||
}
|
||||
|
||||
public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
|
||||
this.algId = algId;
|
||||
signature = new DEROctetString(sig);
|
||||
}
|
||||
|
||||
public void setCertificate(X509Certificate cert)
|
||||
throws CertificateEncodingException, IOException {
|
||||
ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
|
||||
certificate = s.readObject();
|
||||
publicKey = cert.getPublicKey();
|
||||
}
|
||||
|
||||
public byte[] sign(PrivateKey key, InputStream is, int len) throws Exception {
|
||||
Signature signer = Signature.getInstance(CryptoUtils.getSignatureAlgorithm(key));
|
||||
signer.initSign(key);
|
||||
int read;
|
||||
byte buffer[] = new byte[4096];
|
||||
while ((read = is.read(buffer, 0, Math.min(len, buffer.length))) > 0) {
|
||||
signer.update(buffer, 0, read);
|
||||
len -= read;
|
||||
}
|
||||
signer.update(getEncodedAuthenticatedAttributes());
|
||||
return signer.sign();
|
||||
}
|
||||
|
||||
public boolean verify(byte[] image, int length) throws Exception {
|
||||
if (this.length.getValue().intValue() != length) {
|
||||
throw new IllegalArgumentException("Invalid image length");
|
||||
}
|
||||
String algName = CryptoUtils.ID_TO_ALG.get(algId.getAlgorithm().getId());
|
||||
if (algName == null) {
|
||||
throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm());
|
||||
}
|
||||
Signature verifier = Signature.getInstance(algName);
|
||||
verifier.initVerify(publicKey);
|
||||
verifier.update(image, 0, length);
|
||||
verifier.update(getEncodedAuthenticatedAttributes());
|
||||
return verifier.verify(signature.getOctets());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASN1Primitive toASN1Primitive() {
|
||||
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||
v.add(formatVersion);
|
||||
v.add(certificate);
|
||||
v.add(algId);
|
||||
v.add(getAuthenticatedAttributes());
|
||||
v.add(signature);
|
||||
return new DERSequence(v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -64,19 +64,6 @@ private fun PrintStream.byteField(name: String, bytes: ByteArray) {
|
||||
println("}")
|
||||
}
|
||||
|
||||
fun genKeyData(keysDir: File, outSrc: File) {
|
||||
outSrc.parentFile.mkdirs()
|
||||
PrintStream(outSrc).use {
|
||||
it.println("package com.topjohnwu.magisk.signing;")
|
||||
it.println("public final class KeyData {")
|
||||
|
||||
it.byteField("verityCert", File(keysDir, "verity.x509.pem").readBytes())
|
||||
it.byteField("verityKey", File(keysDir, "verity.pk8").readBytes())
|
||||
|
||||
it.println("}")
|
||||
}
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
abstract class ManifestUpdater: DefaultTask() {
|
||||
@get:Input
|
||||
|
@ -305,19 +305,6 @@ fun Project.setupApp() {
|
||||
}
|
||||
}
|
||||
mergeAssetsProvider.configure { dependsOn(syncAssets) }
|
||||
|
||||
val keysDir = rootProject.file("tools/keys")
|
||||
val outSrcDir = File(buildDir, "generated/source/keydata/$name")
|
||||
val outSrc = File(outSrcDir, "com/topjohnwu/magisk/signing/KeyData.java")
|
||||
|
||||
val genSrcTask = tasks.register("generate${variantCapped}KeyData") {
|
||||
inputs.dir(keysDir)
|
||||
outputs.file(outSrc)
|
||||
doLast {
|
||||
genKeyData(keysDir, outSrc)
|
||||
}
|
||||
}
|
||||
registerJavaGeneratingTask(genSrcTask, outSrcDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,6 @@ main() {
|
||||
ui_print "- Device platform: $ABI"
|
||||
|
||||
remove_system_su
|
||||
find_magisk_apk
|
||||
install_magisk
|
||||
|
||||
# Cleanups
|
||||
|
@ -11,7 +11,6 @@
|
||||
umask 022
|
||||
|
||||
OUTFD=$2
|
||||
APK="$3"
|
||||
COMMONDIR=$INSTALLER/assets
|
||||
CHROMEDIR=$INSTALLER/assets/chromeos
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
umask 022
|
||||
|
||||
OUTFD=$2
|
||||
APK="$3"
|
||||
COMMONDIR=$INSTALLER/assets
|
||||
CHROMEDIR=$INSTALLER/assets/chromeos
|
||||
|
||||
|
@ -185,7 +185,7 @@ recovery_actions() {
|
||||
recovery_cleanup() {
|
||||
local DIR
|
||||
ui_print "- Unmounting partitions"
|
||||
(umount_apex
|
||||
(
|
||||
if [ ! -d /postinstall/tmp ]; then
|
||||
umount -l /system
|
||||
umount -l /system_root
|
||||
@ -199,7 +199,8 @@ recovery_cleanup() {
|
||||
mv -f ${DIR}_link $DIR
|
||||
fi
|
||||
done
|
||||
umount -l /dev/random) 2>/dev/null
|
||||
umount -l /dev/random
|
||||
) 2>/dev/null
|
||||
[ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB
|
||||
[ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE
|
||||
[ -z $OLD_LD_CFG ] || export LD_CONFIG_FILE=$OLD_LD_CFG
|
||||
@ -306,92 +307,7 @@ mount_partitions() {
|
||||
SYSTEM_ROOT=false
|
||||
grep ' / ' /proc/mounts | grep -qv 'rootfs' || grep -q ' /system_root ' /proc/mounts && SYSTEM_ROOT=true
|
||||
fi
|
||||
# /vendor is used only on some older devices for recovery AVBv1 signing so is not critical if fails
|
||||
[ -L /system/vendor ] && mount_name vendor$SLOT /vendor '-o ro'
|
||||
$SYSTEM_ROOT && ui_print "- Device is system-as-root"
|
||||
|
||||
# Allow /system/bin commands (dalvikvm) on Android 10+ in recovery
|
||||
$BOOTMODE || mount_apex
|
||||
}
|
||||
|
||||
# loop_setup <ext4_img>, sets LOOPDEV
|
||||
loop_setup() {
|
||||
unset LOOPDEV
|
||||
local LOOP
|
||||
local MINORX=1
|
||||
[ -e /dev/block/loop1 ] && MINORX=$(stat -Lc '%T' /dev/block/loop1)
|
||||
local NUM=0
|
||||
while [ $NUM -lt 64 ]; do
|
||||
LOOP=/dev/block/loop$NUM
|
||||
[ -e $LOOP ] || mknod $LOOP b 7 $((NUM * MINORX))
|
||||
if losetup $LOOP "$1" 2>/dev/null; then
|
||||
LOOPDEV=$LOOP
|
||||
break
|
||||
fi
|
||||
NUM=$((NUM + 1))
|
||||
done
|
||||
}
|
||||
|
||||
mount_apex() {
|
||||
$BOOTMODE || [ ! -d /system/apex ] && return
|
||||
local APEX DEST
|
||||
setup_mntpoint /apex
|
||||
mount -t tmpfs tmpfs /apex -o mode=755
|
||||
local PATTERN='s/.*"name":[^"]*"\([^"]*\).*/\1/p'
|
||||
for APEX in /system/apex/*; do
|
||||
if [ -f $APEX ]; then
|
||||
# handle CAPEX APKs, extract actual APEX APK first
|
||||
unzip -qo $APEX original_apex -d /apex
|
||||
[ -f /apex/original_apex ] && APEX=/apex/original_apex # unzip doesn't do return codes
|
||||
# APEX APKs, extract and loop mount
|
||||
unzip -qo $APEX apex_payload.img -d /apex
|
||||
DEST=$(unzip -qp $APEX apex_manifest.pb | strings | head -n 1 | tr -dc '[:alnum:].-_\n')
|
||||
[ -z $DEST ] && DEST=$(unzip -qp $APEX apex_manifest.json | sed -n $PATTERN)
|
||||
[ -z $DEST ] && continue
|
||||
DEST=/apex/$DEST
|
||||
mkdir -p $DEST
|
||||
loop_setup /apex/apex_payload.img
|
||||
if [ ! -z $LOOPDEV ]; then
|
||||
ui_print "- Mounting $DEST"
|
||||
mount -t ext4 -o ro,noatime $LOOPDEV $DEST
|
||||
fi
|
||||
rm -f /apex/original_apex /apex/apex_payload.img
|
||||
elif [ -d $APEX ]; then
|
||||
# APEX folders, bind mount directory
|
||||
if [ -f $APEX/apex_manifest.json ]; then
|
||||
DEST=/apex/$(sed -n $PATTERN $APEX/apex_manifest.json)
|
||||
elif [ -f $APEX/apex_manifest.pb ]; then
|
||||
DEST=/apex/$(strings $APEX/apex_manifest.pb | head -n 1 | tr -dc '[:alnum:].-_\n')
|
||||
else
|
||||
continue
|
||||
fi
|
||||
mkdir -p $DEST
|
||||
ui_print "- Mounting $DEST"
|
||||
mount -o bind $APEX $DEST
|
||||
fi
|
||||
done
|
||||
export ANDROID_RUNTIME_ROOT=/apex/com.android.runtime
|
||||
export ANDROID_TZDATA_ROOT=/apex/com.android.tzdata
|
||||
export ANDROID_ART_ROOT=/apex/com.android.art
|
||||
export ANDROID_I18N_ROOT=/apex/com.android.i18n
|
||||
local APEXJARS=$(find /apex -name '*.jar' | sort | tr '\n' ':')
|
||||
local FWK=/system/framework
|
||||
export BOOTCLASSPATH=${APEXJARS}\
|
||||
$FWK/framework.jar:$FWK/ext.jar:$FWK/telephony-common.jar:\
|
||||
$FWK/voip-common.jar:$FWK/ims-common.jar:$FWK/telephony-ext.jar
|
||||
}
|
||||
|
||||
umount_apex() {
|
||||
[ -d /apex ] || return
|
||||
umount -l /apex
|
||||
for loop in /dev/block/loop*; do
|
||||
losetup -d $loop 2>/dev/null
|
||||
done
|
||||
unset ANDROID_RUNTIME_ROOT
|
||||
unset ANDROID_TZDATA_ROOT
|
||||
unset ANDROID_ART_ROOT
|
||||
unset ANDROID_I18N_ROOT
|
||||
unset BOOTCLASSPATH
|
||||
}
|
||||
|
||||
# After calling this method, the following variables will be set:
|
||||
@ -452,16 +368,11 @@ find_boot_image() {
|
||||
}
|
||||
|
||||
flash_image() {
|
||||
local CMD1
|
||||
case "$1" in
|
||||
*.gz) CMD1="gzip -d < '$1' 2>/dev/null";;
|
||||
*) CMD1="cat '$1'";;
|
||||
esac
|
||||
if $BOOTSIGNED; then
|
||||
CMD2="$BOOTSIGNER -sign"
|
||||
ui_print "- Sign image with verity keys"
|
||||
else
|
||||
CMD2="cat -"
|
||||
fi
|
||||
if [ -b "$2" ]; then
|
||||
local img_sz=$(stat -c '%s' "$1")
|
||||
local blk_sz=$(blockdev --getsize64 "$2")
|
||||
@ -469,13 +380,13 @@ flash_image() {
|
||||
blockdev --setrw "$2"
|
||||
local blk_ro=$(blockdev --getro "$2")
|
||||
[ "$blk_ro" -eq 1 ] && return 2
|
||||
eval "$CMD1" | eval "$CMD2" | cat - /dev/zero > "$2" 2>/dev/null
|
||||
eval "$CMD1" | cat - /dev/zero > "$2" 2>/dev/null
|
||||
elif [ -c "$2" ]; then
|
||||
flash_eraseall "$2" >&2
|
||||
eval "$CMD1" | eval "$CMD2" | nandwrite -p "$2" - >&2
|
||||
eval "$CMD1" | nandwrite -p "$2" - >&2
|
||||
else
|
||||
ui_print "- Not block or char device, storing image"
|
||||
eval "$CMD1" | eval "$CMD2" > "$2" 2>/dev/null
|
||||
eval "$CMD1" > "$2" 2>/dev/null
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
@ -484,11 +395,6 @@ flash_image() {
|
||||
install_magisk() {
|
||||
cd $MAGISKBIN
|
||||
|
||||
if [ ! -c $BOOTIMAGE ]; then
|
||||
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
|
||||
$BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0"
|
||||
fi
|
||||
|
||||
# Source the boot patcher
|
||||
SOURCEDMODE=true
|
||||
. ./boot_patch.sh "$BOOTIMAGE"
|
||||
@ -596,19 +502,6 @@ check_data() {
|
||||
$DATA_DE && set_nvbase "/data/adb"
|
||||
}
|
||||
|
||||
find_magisk_apk() {
|
||||
local DBAPK
|
||||
[ -z $APK ] && APK=/data/app/com.topjohnwu.magisk*/base.apk
|
||||
[ -f $APK ] || APK=/data/app/*/com.topjohnwu.magisk*/base.apk
|
||||
if [ ! -f $APK ]; then
|
||||
DBAPK=$(magisk --sqlite "SELECT value FROM strings WHERE key='requester'" 2>/dev/null | cut -d= -f2)
|
||||
[ -z $DBAPK ] && DBAPK=$(strings $NVBASE/magisk.db | grep -oE 'requester..*' | cut -c10-)
|
||||
[ -z $DBAPK ] || APK=/data/user_de/0/$DBAPK/dyn/current.apk
|
||||
[ -f $APK ] || [ -z $DBAPK ] || APK=/data/data/$DBAPK/dyn/current.apk
|
||||
fi
|
||||
[ -f $APK ] || ui_print "! Unable to detect Magisk app APK for BootSigner"
|
||||
}
|
||||
|
||||
run_migrations() {
|
||||
local LOCSHA1
|
||||
local TARGET
|
||||
@ -830,8 +723,3 @@ install_module() {
|
||||
|
||||
TMPDIR=/dev/tmp
|
||||
set_nvbase "/data/adb"
|
||||
|
||||
# Bootsigner related stuff
|
||||
BOOTSIGNERCLASS=com.topjohnwu.magisk.signing.SignBoot
|
||||
BOOTSIGNER='/system/bin/dalvikvm -Xnoimage-dex2oat -cp $APK $BOOTSIGNERCLASS'
|
||||
BOOTSIGNED=false
|
||||
|
Loading…
x
Reference in New Issue
Block a user