From ac7467fb59aee2c158816ec875276157ebaa9153 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 18 Sep 2018 23:48:21 -0400 Subject: [PATCH] Optimize boot signing to use as little memory as possible --- .../magisk/container/BaseModule.java | 1 - .../java/com/topjohnwu/utils/CryptoUtils.java | 26 +----- .../java/com/topjohnwu/utils/SignBoot.java | 81 +++++++++---------- 3 files changed, 41 insertions(+), 67 deletions(-) diff --git a/app/src/full/java/com/topjohnwu/magisk/container/BaseModule.java b/app/src/full/java/com/topjohnwu/magisk/container/BaseModule.java index 194f7e002..aa2576b5c 100644 --- a/app/src/full/java/com/topjohnwu/magisk/container/BaseModule.java +++ b/app/src/full/java/com/topjohnwu/magisk/container/BaseModule.java @@ -2,7 +2,6 @@ package com.topjohnwu.magisk.container; import android.content.ContentValues; import android.database.Cursor; -import android.text.TextUtils; import java.util.List; diff --git a/utils/src/main/java/com/topjohnwu/utils/CryptoUtils.java b/utils/src/main/java/com/topjohnwu/utils/CryptoUtils.java index 3d3dacdc3..6492c071f 100644 --- a/utils/src/main/java/com/topjohnwu/utils/CryptoUtils.java +++ b/utils/src/main/java/com/topjohnwu/utils/CryptoUtils.java @@ -15,7 +15,6 @@ import java.security.Key; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.Signature; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.ECPrivateKeySpec; @@ -27,8 +26,8 @@ import java.util.Map; class CryptoUtils { - private static final Map ID_TO_ALG; - private static final Map ALG_TO_ID; + static final Map ID_TO_ALG; + static final Map ALG_TO_ID; static { ID_TO_ALG = new HashMap<>(); @@ -47,7 +46,7 @@ class CryptoUtils { 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())) { int curveSize; KeyFactory factory = KeyFactory.getInstance("EC"); @@ -82,25 +81,6 @@ class CryptoUtils { 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) throws IOException, GeneralSecurityException { try { diff --git a/utils/src/main/java/com/topjohnwu/utils/SignBoot.java b/utils/src/main/java/com/topjohnwu/utils/SignBoot.java index 8f16cc26c..5b482c56d 100644 --- a/utils/src/main/java/com/topjohnwu/utils/SignBoot.java +++ b/utils/src/main/java/com/topjohnwu/utils/SignBoot.java @@ -23,6 +23,7 @@ import java.nio.ByteOrder; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; +import java.security.Signature; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -37,20 +38,17 @@ public class SignBoot { public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut, InputStream cert, InputStream key) { try { - ByteArrayStream bas = new ByteArrayStream(); - bas.readFrom(imgIn); - byte[] image = bas.toByteArray(); - bas.close(); - int signableSize = getSignableImageSize(image); - if (signableSize < image.length) { + ByteArrayStream image = new ByteArrayStream(); + image.readFrom(imgIn); + int signableSize = getSignableImageSize(image.getBuf()); + if (signableSize < image.size()) { System.err.println("NOTE: truncating input from " + - image.length + " to " + signableSize + " bytes"); - image = Arrays.copyOf(image, signableSize); - } else if (signableSize > image.length) { + image.size() + " to " + signableSize + " bytes"); + } else if (signableSize > image.size()) { throw new IllegalArgumentException("Invalid image: too short, expected " + signableSize + " bytes"); } - BootSignature bootsig = new BootSignature(target, image.length); + BootSignature bootsig = new BootSignature(target, image.size()); if (cert == null) { cert = SignBoot.class.getResourceAsStream("/keys/testkey.x509.pem"); } @@ -60,10 +58,10 @@ public class SignBoot { key = SignBoot.class.getResourceAsStream("/keys/testkey.pk8"); } PrivateKey privateKey = CryptoUtils.readPrivateKey(key); - bootsig.setSignature(bootsig.sign(image, privateKey), + bootsig.setSignature(bootsig.sign(privateKey, image.getBuf(), signableSize), CryptoUtils.getSignatureAlgorithmIdentifier(privateKey)); byte[] encoded_bootsig = bootsig.getEncoded(); - imgOut.write(image); + image.writeTo(imgOut); imgOut.write(encoded_bootsig); imgOut.flush(); return true; @@ -75,21 +73,19 @@ public class SignBoot { public static boolean verifySignature(InputStream imgIn, InputStream certIn) { try { - ByteArrayStream bas = new ByteArrayStream(); - bas.readFrom(imgIn); - byte[] image = bas.toByteArray(); - bas.close(); - int signableSize = getSignableImageSize(image); - if (signableSize >= image.length) { + ByteArrayStream image = new ByteArrayStream(); + image.readFrom(imgIn); + int signableSize = getSignableImageSize(image.getBuf()); + if (signableSize >= image.size()) { System.err.println("Invalid image: not signed"); return false; } - byte[] signature = Arrays.copyOfRange(image, signableSize, image.length); + byte[] signature = Arrays.copyOfRange(image.getBuf(), signableSize, image.size()); BootSignature bootsig = new BootSignature(signature); if (certIn != null) { bootsig.setCertificate(CryptoUtils.readCertificate(certIn)); } - if (bootsig.verify(Arrays.copyOf(image, signableSize))) { + if (bootsig.verify(image.getBuf(), signableSize)) { System.err.println("Signature is VALID"); return true; } else { @@ -130,7 +126,7 @@ public class SignBoot { static class BootSignature extends ASN1Object { private ASN1Integer formatVersion; private ASN1Encodable certificate; - private AlgorithmIdentifier algorithmIdentifier; + private AlgorithmIdentifier algId; private DERPrintableString target; private ASN1Integer length; private DEROctetString signature; @@ -167,8 +163,7 @@ public class SignBoot { X509Certificate c = (X509Certificate) cf.generateCertificate(bis); publicKey = c.getPublicKey(); ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2); - algorithmIdentifier = new AlgorithmIdentifier( - (ASN1ObjectIdentifier) algId.getObjectAt(0)); + this.algId = new AlgorithmIdentifier((ASN1ObjectIdentifier) algId.getObjectAt(0)); ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3); target = (DERPrintableString) attrs.getObjectAt(0); length = (ASN1Integer) attrs.getObjectAt(1); @@ -187,38 +182,38 @@ public class SignBoot { } public void setSignature(byte[] sig, AlgorithmIdentifier algId) { - algorithmIdentifier = algId; + this.algId = algId; signature = new DEROctetString(sig); } public void setCertificate(X509Certificate cert) - throws Exception, IOException, CertificateEncodingException { + throws CertificateEncodingException, IOException { ASN1InputStream s = new ASN1InputStream(cert.getEncoded()); certificate = s.readObject(); publicKey = cert.getPublicKey(); } - public byte[] generateSignableImage(byte[] image) throws IOException { - byte[] attrs = getEncodedAuthenticatedAttributes(); - byte[] signable = Arrays.copyOf(image, image.length + attrs.length); - for (int i=0; i < attrs.length; i++) { - signable[i+image.length] = attrs[i]; - } - return signable; + public byte[] sign(PrivateKey key, byte[] image, int length) throws Exception { + Signature signer = Signature.getInstance(CryptoUtils.getSignatureAlgorithm(key)); + signer.initSign(key); + signer.update(image, 0, length); + signer.update(getEncodedAuthenticatedAttributes()); + return signer.sign(); } - public byte[] sign(byte[] image, PrivateKey key) throws Exception { - byte[] signable = generateSignableImage(image); - return CryptoUtils.sign(key, signable); - } - - public boolean verify(byte[] image) throws Exception { - if (length.getValue().intValue() != image.length) { + public boolean verify(byte[] image, int length) throws Exception { + if (this.length.getValue().intValue() != length) { throw new IllegalArgumentException("Invalid image length"); } - byte[] signable = generateSignableImage(image); - return CryptoUtils.verify(publicKey, signable, signature.getOctets(), - algorithmIdentifier); + 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 @@ -226,7 +221,7 @@ public class SignBoot { ASN1EncodableVector v = new ASN1EncodableVector(); v.add(formatVersion); v.add(certificate); - v.add(algorithmIdentifier); + v.add(algId); v.add(getAuthenticatedAttributes()); v.add(signature); return new DERSequence(v);