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

Commit 6b7efbc4 authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

V4 uses V2 digest if V3 is not available.

Test: atest PackageManagerShellCommandIncrementalTest
Bug: b/151240006
Change-Id: I242b599e434880ce218537574e879e9436e5d3da
parent 39ec0d54
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -72,16 +72,16 @@ public class V4Signature {
     * V4 signature data.
     */
    public static class SigningInfo {
        public final byte[] v3Digest;  // used to match with the corresponding APK
        public final byte[] apkDigest;  // used to match with the corresponding APK
        public final byte[] certificate; // ASN.1 DER form
        public final byte[] additionalData; // a free-form binary data blob
        public final byte[] publicKey; // ASN.1 DER, must match the certificate
        public final int signatureAlgorithmId; // see the APK v2 doc for the list
        public final byte[] signature;

        SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
        SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
                byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
            this.v3Digest = v3Digest;
            this.apkDigest = apkDigest;
            this.certificate = certificate;
            this.additionalData = additionalData;
            this.publicKey = publicKey;
@@ -94,13 +94,13 @@ public class V4Signature {
         */
        public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
            ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
            byte[] v3Digest = readBytes(buffer);
            byte[] apkDigest = readBytes(buffer);
            byte[] certificate = readBytes(buffer);
            byte[] additionalData = readBytes(buffer);
            byte[] publicKey = readBytes(buffer);
            int signatureAlgorithmId = buffer.getInt();
            byte[] signature = readBytes(buffer);
            return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
            return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
                    signatureAlgorithmId, signature);
        }
    }
@@ -150,7 +150,7 @@ public class V4Signature {
        final int size =
                4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
                        hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
                        signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
                        signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
                        signingInfo.additionalData);
        ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
        buffer.putInt(size);
@@ -159,7 +159,7 @@ public class V4Signature {
        buffer.put(hashingInfo.log2BlockSize);
        writeBytes(buffer, hashingInfo.salt);
        writeBytes(buffer, hashingInfo.rawRootHash);
        writeBytes(buffer, signingInfo.v3Digest);
        writeBytes(buffer, signingInfo.apkDigest);
        writeBytes(buffer, signingInfo.certificate);
        writeBytes(buffer, signingInfo.additionalData);
        return buffer.array();
+12 −3
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;

import android.util.ArrayMap;
@@ -117,7 +118,10 @@ public class ApkSignatureSchemeV2Verifier {
        return vSigner.certs;
    }

    private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
    /**
     * Same as above returns the full signer object, containing additional info e.g. digest.
     */
    public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
            throws SignatureNotFoundException, SecurityException, IOException {
        try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
            return verify(apk, verifyIntegrity);
@@ -209,9 +213,11 @@ public class ApkSignatureSchemeV2Verifier {
                    verityDigest, apk.length(), signatureInfo);
        }

        byte[] digest = pickBestDigestForV4(contentDigests);

        return new VerifiedSigner(
                signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
                verityRootHash);
                verityRootHash, digest);
    }

    private static X509Certificate[] verifySigner(
@@ -426,11 +432,14 @@ public class ApkSignatureSchemeV2Verifier {
     */
    public static class VerifiedSigner {
        public final X509Certificate[][] certs;

        public final byte[] verityRootHash;
        public final byte[] digest;

        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
            this.certs = certs;
            this.verityRootHash = verityRootHash;
            this.digest = digest;
        }

    }
+2 −16
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package android.util.apk;

import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
@@ -26,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;

import android.os.Build;
@@ -213,24 +212,11 @@ public class ApkSignatureSchemeV3Verifier {
                    verityDigest, apk.length(), signatureInfo);
        }

        result.digest = pickBestV3DigestForV4(contentDigests);
        result.digest = pickBestDigestForV4(contentDigests);

        return result;
    }

    // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
    private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
        final int[] orderedContentDigestTypes =
                {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
                        CONTENT_DIGEST_CHUNKED_SHA256};
        for (int contentDigestType : orderedContentDigestTypes) {
            if (contentDigests.containsKey(contentDigestType)) {
                return contentDigests.get(contentDigestType);
            }
        }
        return null;
    }

    private static VerifiedSigner verifySigner(
            ByteBuffer signerBlock,
            Map<Integer, byte[]> contentDigests,
+4 −4
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ public class ApkSignatureSchemeV4Verifier {
                    "Public key mismatch between certificate and signature record");
        }

        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest);
        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
    }

    /**
@@ -155,11 +155,11 @@ public class ApkSignatureSchemeV4Verifier {
     */
    public static class VerifiedSigner {
        public final Certificate[] certs;
        public byte[] v3Digest;
        public byte[] apkDigest;

        public VerifiedSigner(Certificate[] certs, byte[] v3Digest) {
        public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
            this.certs = certs;
            this.v3Digest = v3Digest;
            this.apkDigest = apkDigest;
        }

    }
+29 −11
Original line number Diff line number Diff line
@@ -184,27 +184,45 @@ public class ApkSignatureVerifier {
            Signature[] signerSigs = convertToSignatures(signerCerts);

            if (verifyFull) {
                // v4 is an add-on and requires v3 signature to validate against its certificates
                ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
                byte[] nonstreamingDigest = null;
                Certificate[][] nonstreamingCerts = null;

                try {
                    // v4 is an add-on and requires v2 or v3 signature to validate against its
                    // certificate and digest
                    ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
                            ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
                Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
                Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
                    nonstreamingDigest = v3Signer.digest;
                    nonstreamingCerts = new Certificate[][]{v3Signer.certs};
                } catch (SignatureNotFoundException e) {
                    try {
                        ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
                                ApkSignatureSchemeV2Verifier.verify(apkPath, false);
                        nonstreamingDigest = v2Signer.digest;
                        nonstreamingCerts = v2Signer.certs;
                    } catch (SignatureNotFoundException ee) {
                        throw new SecurityException(
                                "V4 verification failed to collect V2/V3 certificates from : "
                                        + apkPath, ee);
                    }
                }

                Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
                if (nonstreamingSigs.length != signerSigs.length) {
                    throw new SecurityException(
                            "Invalid number of certificates: " + nonstreaming.certs.length);
                            "Invalid number of certificates: " + nonstreamingSigs.length);
                }

                for (int i = 0, size = signerSigs.length; i < size; ++i) {
                    if (!nonstreamingSigs[i].equals(signerSigs[i])) {
                        throw new SecurityException("V4 signature certificate does not match V3");
                        throw new SecurityException(
                                "V4 signature certificate does not match V2/V3");
                    }
                }

                // TODO(b/151240006): add support for v2 digest and make it mandatory.
                if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
                        nonstreaming.digest, vSigner.v3Digest.length)) {
                    throw new SecurityException("V3 digest in V4 signature does not match V3");
                if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
                        vSigner.apkDigest.length)) {
                    throw new SecurityException("APK digest in V4 signature does not match V2/V3");
                }
            }

Loading