Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 198b9f4f authored by Daniel Colascione's avatar Daniel Colascione Committed by Gerrit Code Review
Browse files

Merge "Add zip hint generation support to signapk tool"

parents da187dd3 334ece99
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.signapk;
import java.io.OutputStream;
import java.io.IOException;

class CountingOutputStream extends OutputStream {
    private final OutputStream mBase;
    private long mWrittenBytes;

    public CountingOutputStream(OutputStream base) {
        mBase = base;
    }

    @Override
    public void close() throws IOException {
        mBase.close();
    }

    @Override
    public void flush() throws IOException {
        mBase.flush();
    }

    @Override
    public void write(byte[] b) throws IOException {
        mBase.write(b);
        mWrittenBytes += b.length;
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        mBase.write(b, off, len);
        mWrittenBytes += len;
    }

    @Override
    public void write(int b) throws IOException {
        mBase.write(b);
        mWrittenBytes += 1;
    }

    public long getWrittenBytes() {
        return mWrittenBytes;
    }
}
+73 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import org.conscrypt.OpenSSLProvider;

import com.android.apksig.ApkSignerEngine;
import com.android.apksig.DefaultApkSignerEngine;
import com.android.apksig.Hints;
import com.android.apksig.apk.ApkUtils;
import com.android.apksig.apk.MinSdkVersionException;
import com.android.apksig.util.DataSink;
@@ -73,6 +74,7 @@ import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
@@ -80,6 +82,7 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;

import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
@@ -372,11 +375,16 @@ class SignApk {
            Pattern ignoredFilenamePattern,
            ApkSignerEngine apkSigner,
            JarOutputStream out,
            CountingOutputStream outCounter,
            long timestamp,
            int defaultAlignment) throws IOException {
        byte[] buffer = new byte[4096];
        int num;

        List<Pattern> pinPatterns = extractPinPatterns(in);
        ArrayList<Hints.ByteRange> pinByteRanges = pinPatterns == null ? null : new ArrayList<>();
        HashSet<String> namesToPin = new HashSet<>();

        ArrayList<String> names = new ArrayList<String>();
        for (Enumeration<JarEntry> e = in.entries(); e.hasMoreElements();) {
            JarEntry entry = e.nextElement();
@@ -388,6 +396,16 @@ class SignApk {
                    && (ignoredFilenamePattern.matcher(entryName).matches())) {
                continue;
            }
            if (Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME.equals(entryName)) {
                continue;  // We regenerate it below.
            }
            if (pinPatterns != null) {
                for (Pattern pinPattern : pinPatterns) {
                    if (pinPattern.matcher(entryName).matches()) {
                        namesToPin.add(entryName);
                    }
                }
            }
            names.add(entryName);
        }
        Collections.sort(names);
@@ -460,6 +478,7 @@ class SignApk {
            outEntry.setExtra(extra);
            offset += extra.length;

            long entryHeaderStart = outCounter.getWrittenBytes();
            out.putNextEntry(outEntry);
            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =
                    (apkSigner != null) ? apkSigner.outputJarEntry(name) : null;
@@ -475,10 +494,18 @@ class SignApk {
                    offset += num;
                }
            }
            out.closeEntry();
            out.flush();
            if (inspectEntryRequest != null) {
                inspectEntryRequest.done();
            }

            if (namesToPin.contains(name)) {
                pinByteRanges.add(
                    new Hints.ByteRange(
                        entryHeaderStart,
                        outCounter.getWrittenBytes()));
            }
        }

        // Copy all the non-STORED entries.  We don't attempt to
@@ -494,6 +521,7 @@ class SignApk {
            // Create a new entry so that the compressed len is recomputed.
            JarEntry outEntry = new JarEntry(name);
            outEntry.setTime(timestamp);
            long entryHeaderStart = outCounter.getWrittenBytes();
            out.putNextEntry(outEntry);
            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =
                    (apkSigner != null) ? apkSigner.outputJarEntry(name) : null;
@@ -507,11 +535,47 @@ class SignApk {
                    entryDataSink.consume(buffer, 0, num);
                }
            }
            out.closeEntry();
            out.flush();
            if (inspectEntryRequest != null) {
                inspectEntryRequest.done();
            }

            if (namesToPin.contains(name)) {
                pinByteRanges.add(
                    new Hints.ByteRange(
                        entryHeaderStart,
                        outCounter.getWrittenBytes()));
            }
        }

        if (pinByteRanges != null) {
            // Cover central directory
            pinByteRanges.add(
                new Hints.ByteRange(outCounter.getWrittenBytes(),
                                    Long.MAX_VALUE));
            addPinByteRanges(out, pinByteRanges, timestamp);
        }
    }

    private static List<Pattern> extractPinPatterns(JarFile in) throws IOException {
        ZipEntry pinMetaEntry = in.getEntry(Hints.PIN_HINT_ASSET_ZIP_ENTRY_NAME);
        if (pinMetaEntry == null) {
            return null;
        }
        InputStream pinMetaStream = in.getInputStream(pinMetaEntry);
        byte[] patternBlob = new byte[(int) pinMetaEntry.getSize()];
        pinMetaStream.read(patternBlob);
        return Hints.parsePinPatterns(patternBlob);
    }

    private static void addPinByteRanges(JarOutputStream outputJar,
                                         ArrayList<Hints.ByteRange> pinByteRanges,
                                         long timestamp) throws IOException {
        JarEntry je = new JarEntry(Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME);
        je.setTime(timestamp);
        outputJar.putNextEntry(je);
        outputJar.write(Hints.encodeByteRangeList(pinByteRanges));
    }

    private static boolean shouldOutputApkEntry(
@@ -679,9 +743,11 @@ class SignApk {
        public void write(OutputStream out) throws IOException {
            try {
                signer = new WholeFileSignerOutputStream(out, outputStream);
                JarOutputStream outputJar = new JarOutputStream(signer);
                CountingOutputStream outputJarCounter = new CountingOutputStream(signer);
                JarOutputStream outputJar = new JarOutputStream(outputJarCounter);

                copyFiles(inputJar, STRIP_PATTERN, null, outputJar, timestamp, 0);
                copyFiles(inputJar, STRIP_PATTERN, null, outputJar,
                          outputJarCounter, timestamp, 0);
                addOtacert(outputJar, publicKeyFile, timestamp);

                signer.notifyClosing();
@@ -1065,11 +1131,14 @@ class SignApk {
                    // Build the output APK in memory, by copying input APK's ZIP entries across
                    // and then signing the output APK.
                    ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
                    JarOutputStream outputJar = new JarOutputStream(v1SignedApkBuf);
                    CountingOutputStream outputJarCounter =
                            new CountingOutputStream(v1SignedApkBuf);
                    JarOutputStream outputJar = new JarOutputStream(outputJarCounter);
                    // Use maximum compression for compressed entries because the APK lives forever
                    // on the system partition.
                    outputJar.setLevel(9);
                    copyFiles(inputJar, null, apkSigner, outputJar, timestamp, alignment);
                    copyFiles(inputJar, null, apkSigner, outputJar,
                              outputJarCounter, timestamp, alignment);
                    ApkSignerEngine.OutputJarSignatureRequest addV1SignatureRequest =
                            apkSigner.outputJarEntries();
                    if (addV1SignatureRequest != null) {