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

Commit c6f4f747 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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

parents 30b6e5c7 6b7efbc4
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