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

Commit 6b9d6879 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "V4 uses V2 digest if V3 is not available." into rvc-dev am: c6f4f747

Change-Id: I5cd38afc242e6bc42a6eac708f638a0c5b086b34
parents 3334c767 c6f4f747
Loading
Loading
Loading
Loading
+7 −7
Original line number Original line Diff line number Diff line
@@ -72,16 +72,16 @@ public class V4Signature {
     * V4 signature data.
     * V4 signature data.
     */
     */
    public static class SigningInfo {
    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[] certificate; // ASN.1 DER form
        public final byte[] additionalData; // a free-form binary data blob
        public final byte[] additionalData; // a free-form binary data blob
        public final byte[] publicKey; // ASN.1 DER, must match the certificate
        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 int signatureAlgorithmId; // see the APK v2 doc for the list
        public final byte[] signature;
        public final byte[] signature;


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


import android.util.ArrayMap;
import android.util.ArrayMap;
@@ -117,7 +118,10 @@ public class ApkSignatureSchemeV2Verifier {
        return vSigner.certs;
        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 {
            throws SignatureNotFoundException, SecurityException, IOException {
        try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
        try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
            return verify(apk, verifyIntegrity);
            return verify(apk, verifyIntegrity);
@@ -209,9 +213,11 @@ public class ApkSignatureSchemeV2Verifier {
                    verityDigest, apk.length(), signatureInfo);
                    verityDigest, apk.length(), signatureInfo);
        }
        }


        byte[] digest = pickBestDigestForV4(contentDigests);

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


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

        public final byte[] verityRootHash;
        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.certs = certs;
            this.verityRootHash = verityRootHash;
            this.verityRootHash = verityRootHash;
            this.digest = digest;
        }
        }


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


package android.util.apk;
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.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
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.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;


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


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


        return result;
        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(
    private static VerifiedSigner verifySigner(
            ByteBuffer signerBlock,
            ByteBuffer signerBlock,
            Map<Integer, byte[]> contentDigests,
            Map<Integer, byte[]> contentDigests,
+4 −4
Original line number Original line Diff line number Diff line
@@ -145,7 +145,7 @@ public class ApkSignatureSchemeV4Verifier {
                    "Public key mismatch between certificate and signature record");
                    "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 static class VerifiedSigner {
        public final Certificate[] certs;
        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.certs = certs;
            this.v3Digest = v3Digest;
            this.apkDigest = apkDigest;
        }
        }


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


            if (verifyFull) {
            if (verifyFull) {
                // v4 is an add-on and requires v3 signature to validate against its certificates
                byte[] nonstreamingDigest = null;
                ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
                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);
                            ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
                Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
                    nonstreamingDigest = v3Signer.digest;
                Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
                    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) {
                if (nonstreamingSigs.length != signerSigs.length) {
                    throw new SecurityException(
                    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) {
                for (int i = 0, size = signerSigs.length; i < size; ++i) {
                    if (!nonstreamingSigs[i].equals(signerSigs[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.equals(vSigner.apkDigest, nonstreamingDigest,
                if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
                        vSigner.apkDigest.length)) {
                        nonstreaming.digest, vSigner.v3Digest.length)) {
                    throw new SecurityException("APK digest in V4 signature does not match V2/V3");
                    throw new SecurityException("V3 digest in V4 signature does not match V3");
                }
                }
            }
            }


Loading