mirror of
https://github.com/topjohnwu/Magisk
synced 2024-11-13 20:54:12 +01:00
Optimize boot signing to use as little memory as possible
This commit is contained in:
parent
2c0436216f
commit
ac7467fb59
@ -2,7 +2,6 @@ package com.topjohnwu.magisk.container;
|
|||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import java.security.Key;
|
|||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.Signature;
|
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.spec.ECPrivateKeySpec;
|
import java.security.spec.ECPrivateKeySpec;
|
||||||
@ -27,8 +26,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
class CryptoUtils {
|
class CryptoUtils {
|
||||||
|
|
||||||
private static final Map<String, String> ID_TO_ALG;
|
static final Map<String, String> ID_TO_ALG;
|
||||||
private static final Map<String, String> ALG_TO_ID;
|
static final Map<String, String> ALG_TO_ID;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ID_TO_ALG = new HashMap<>();
|
ID_TO_ALG = new HashMap<>();
|
||||||
@ -47,7 +46,7 @@ class CryptoUtils {
|
|||||||
ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId());
|
ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getSignatureAlgorithm(Key key) throws Exception {
|
static String getSignatureAlgorithm(Key key) throws Exception {
|
||||||
if ("EC".equals(key.getAlgorithm())) {
|
if ("EC".equals(key.getAlgorithm())) {
|
||||||
int curveSize;
|
int curveSize;
|
||||||
KeyFactory factory = KeyFactory.getInstance("EC");
|
KeyFactory factory = KeyFactory.getInstance("EC");
|
||||||
@ -82,25 +81,6 @@ class CryptoUtils {
|
|||||||
return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id));
|
return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean verify(PublicKey key, byte[] input, byte[] signature,
|
|
||||||
AlgorithmIdentifier algId) throws Exception {
|
|
||||||
String algName = ID_TO_ALG.get(algId.getAlgorithm().getId());
|
|
||||||
if (algName == null) {
|
|
||||||
throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm());
|
|
||||||
}
|
|
||||||
Signature verifier = Signature.getInstance(algName);
|
|
||||||
verifier.initVerify(key);
|
|
||||||
verifier.update(input);
|
|
||||||
return verifier.verify(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception {
|
|
||||||
Signature signer = Signature.getInstance(getSignatureAlgorithm(privateKey));
|
|
||||||
signer.initSign(privateKey);
|
|
||||||
signer.update(input);
|
|
||||||
return signer.sign();
|
|
||||||
}
|
|
||||||
|
|
||||||
static X509Certificate readCertificate(InputStream input)
|
static X509Certificate readCertificate(InputStream input)
|
||||||
throws IOException, GeneralSecurityException {
|
throws IOException, GeneralSecurityException {
|
||||||
try {
|
try {
|
||||||
|
@ -23,6 +23,7 @@ import java.nio.ByteOrder;
|
|||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
import java.security.Signature;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
@ -37,20 +38,17 @@ public class SignBoot {
|
|||||||
public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
|
public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
|
||||||
InputStream cert, InputStream key) {
|
InputStream cert, InputStream key) {
|
||||||
try {
|
try {
|
||||||
ByteArrayStream bas = new ByteArrayStream();
|
ByteArrayStream image = new ByteArrayStream();
|
||||||
bas.readFrom(imgIn);
|
image.readFrom(imgIn);
|
||||||
byte[] image = bas.toByteArray();
|
int signableSize = getSignableImageSize(image.getBuf());
|
||||||
bas.close();
|
if (signableSize < image.size()) {
|
||||||
int signableSize = getSignableImageSize(image);
|
|
||||||
if (signableSize < image.length) {
|
|
||||||
System.err.println("NOTE: truncating input from " +
|
System.err.println("NOTE: truncating input from " +
|
||||||
image.length + " to " + signableSize + " bytes");
|
image.size() + " to " + signableSize + " bytes");
|
||||||
image = Arrays.copyOf(image, signableSize);
|
} else if (signableSize > image.size()) {
|
||||||
} else if (signableSize > image.length) {
|
|
||||||
throw new IllegalArgumentException("Invalid image: too short, expected " +
|
throw new IllegalArgumentException("Invalid image: too short, expected " +
|
||||||
signableSize + " bytes");
|
signableSize + " bytes");
|
||||||
}
|
}
|
||||||
BootSignature bootsig = new BootSignature(target, image.length);
|
BootSignature bootsig = new BootSignature(target, image.size());
|
||||||
if (cert == null) {
|
if (cert == null) {
|
||||||
cert = SignBoot.class.getResourceAsStream("/keys/testkey.x509.pem");
|
cert = SignBoot.class.getResourceAsStream("/keys/testkey.x509.pem");
|
||||||
}
|
}
|
||||||
@ -60,10 +58,10 @@ public class SignBoot {
|
|||||||
key = SignBoot.class.getResourceAsStream("/keys/testkey.pk8");
|
key = SignBoot.class.getResourceAsStream("/keys/testkey.pk8");
|
||||||
}
|
}
|
||||||
PrivateKey privateKey = CryptoUtils.readPrivateKey(key);
|
PrivateKey privateKey = CryptoUtils.readPrivateKey(key);
|
||||||
bootsig.setSignature(bootsig.sign(image, privateKey),
|
bootsig.setSignature(bootsig.sign(privateKey, image.getBuf(), signableSize),
|
||||||
CryptoUtils.getSignatureAlgorithmIdentifier(privateKey));
|
CryptoUtils.getSignatureAlgorithmIdentifier(privateKey));
|
||||||
byte[] encoded_bootsig = bootsig.getEncoded();
|
byte[] encoded_bootsig = bootsig.getEncoded();
|
||||||
imgOut.write(image);
|
image.writeTo(imgOut);
|
||||||
imgOut.write(encoded_bootsig);
|
imgOut.write(encoded_bootsig);
|
||||||
imgOut.flush();
|
imgOut.flush();
|
||||||
return true;
|
return true;
|
||||||
@ -75,21 +73,19 @@ public class SignBoot {
|
|||||||
|
|
||||||
public static boolean verifySignature(InputStream imgIn, InputStream certIn) {
|
public static boolean verifySignature(InputStream imgIn, InputStream certIn) {
|
||||||
try {
|
try {
|
||||||
ByteArrayStream bas = new ByteArrayStream();
|
ByteArrayStream image = new ByteArrayStream();
|
||||||
bas.readFrom(imgIn);
|
image.readFrom(imgIn);
|
||||||
byte[] image = bas.toByteArray();
|
int signableSize = getSignableImageSize(image.getBuf());
|
||||||
bas.close();
|
if (signableSize >= image.size()) {
|
||||||
int signableSize = getSignableImageSize(image);
|
|
||||||
if (signableSize >= image.length) {
|
|
||||||
System.err.println("Invalid image: not signed");
|
System.err.println("Invalid image: not signed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
|
byte[] signature = Arrays.copyOfRange(image.getBuf(), signableSize, image.size());
|
||||||
BootSignature bootsig = new BootSignature(signature);
|
BootSignature bootsig = new BootSignature(signature);
|
||||||
if (certIn != null) {
|
if (certIn != null) {
|
||||||
bootsig.setCertificate(CryptoUtils.readCertificate(certIn));
|
bootsig.setCertificate(CryptoUtils.readCertificate(certIn));
|
||||||
}
|
}
|
||||||
if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
|
if (bootsig.verify(image.getBuf(), signableSize)) {
|
||||||
System.err.println("Signature is VALID");
|
System.err.println("Signature is VALID");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -130,7 +126,7 @@ public class SignBoot {
|
|||||||
static class BootSignature extends ASN1Object {
|
static class BootSignature extends ASN1Object {
|
||||||
private ASN1Integer formatVersion;
|
private ASN1Integer formatVersion;
|
||||||
private ASN1Encodable certificate;
|
private ASN1Encodable certificate;
|
||||||
private AlgorithmIdentifier algorithmIdentifier;
|
private AlgorithmIdentifier algId;
|
||||||
private DERPrintableString target;
|
private DERPrintableString target;
|
||||||
private ASN1Integer length;
|
private ASN1Integer length;
|
||||||
private DEROctetString signature;
|
private DEROctetString signature;
|
||||||
@ -167,8 +163,7 @@ public class SignBoot {
|
|||||||
X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
|
X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
|
||||||
publicKey = c.getPublicKey();
|
publicKey = c.getPublicKey();
|
||||||
ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
|
ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
|
||||||
algorithmIdentifier = new AlgorithmIdentifier(
|
this.algId = new AlgorithmIdentifier((ASN1ObjectIdentifier) algId.getObjectAt(0));
|
||||||
(ASN1ObjectIdentifier) algId.getObjectAt(0));
|
|
||||||
ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
|
ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
|
||||||
target = (DERPrintableString) attrs.getObjectAt(0);
|
target = (DERPrintableString) attrs.getObjectAt(0);
|
||||||
length = (ASN1Integer) attrs.getObjectAt(1);
|
length = (ASN1Integer) attrs.getObjectAt(1);
|
||||||
@ -187,38 +182,38 @@ public class SignBoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
|
public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
|
||||||
algorithmIdentifier = algId;
|
this.algId = algId;
|
||||||
signature = new DEROctetString(sig);
|
signature = new DEROctetString(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCertificate(X509Certificate cert)
|
public void setCertificate(X509Certificate cert)
|
||||||
throws Exception, IOException, CertificateEncodingException {
|
throws CertificateEncodingException, IOException {
|
||||||
ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
|
ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
|
||||||
certificate = s.readObject();
|
certificate = s.readObject();
|
||||||
publicKey = cert.getPublicKey();
|
publicKey = cert.getPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] generateSignableImage(byte[] image) throws IOException {
|
public byte[] sign(PrivateKey key, byte[] image, int length) throws Exception {
|
||||||
byte[] attrs = getEncodedAuthenticatedAttributes();
|
Signature signer = Signature.getInstance(CryptoUtils.getSignatureAlgorithm(key));
|
||||||
byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
|
signer.initSign(key);
|
||||||
for (int i=0; i < attrs.length; i++) {
|
signer.update(image, 0, length);
|
||||||
signable[i+image.length] = attrs[i];
|
signer.update(getEncodedAuthenticatedAttributes());
|
||||||
}
|
return signer.sign();
|
||||||
return signable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] sign(byte[] image, PrivateKey key) throws Exception {
|
public boolean verify(byte[] image, int length) throws Exception {
|
||||||
byte[] signable = generateSignableImage(image);
|
if (this.length.getValue().intValue() != length) {
|
||||||
return CryptoUtils.sign(key, signable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(byte[] image) throws Exception {
|
|
||||||
if (length.getValue().intValue() != image.length) {
|
|
||||||
throw new IllegalArgumentException("Invalid image length");
|
throw new IllegalArgumentException("Invalid image length");
|
||||||
}
|
}
|
||||||
byte[] signable = generateSignableImage(image);
|
String algName = CryptoUtils.ID_TO_ALG.get(algId.getAlgorithm().getId());
|
||||||
return CryptoUtils.verify(publicKey, signable, signature.getOctets(),
|
if (algName == null) {
|
||||||
algorithmIdentifier);
|
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
|
@Override
|
||||||
@ -226,7 +221,7 @@ public class SignBoot {
|
|||||||
ASN1EncodableVector v = new ASN1EncodableVector();
|
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||||
v.add(formatVersion);
|
v.add(formatVersion);
|
||||||
v.add(certificate);
|
v.add(certificate);
|
||||||
v.add(algorithmIdentifier);
|
v.add(algId);
|
||||||
v.add(getAuthenticatedAttributes());
|
v.add(getAuthenticatedAttributes());
|
||||||
v.add(signature);
|
v.add(signature);
|
||||||
return new DERSequence(v);
|
return new DERSequence(v);
|
||||||
|
Loading…
Reference in New Issue
Block a user