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

Commit 4ba1eeaa authored by Victor Hsieh's avatar Victor Hsieh
Browse files

Verify the content length in the verity digest

When generating digest for verity, for the last incomplete 4k chunk, the
data is padded with 0s.  This implies that we can not tell from the
digest whether the file contains 0 or not, or how many 0s.

Since the verity hash is used by the kernel, the definition cannot be
change.  Instead, the actual hashed content length is appended to the
original digest and is verified before used.

Also uprev algorithm IDs.

Test: use new apksigner to sign an apk, apk can be installed on device
Bug: 30972906
Change-Id: I382af6e4090c7dc3f92d5acb5ac5d02d1f496992
parent 8c55e8c3
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -213,7 +213,9 @@ public class ApkSignatureSchemeV2Verifier {


        byte[] verityRootHash = null;
        byte[] verityRootHash = null;
        if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
        if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
            verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
            byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
            verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
                    verityDigest, apk.length(), signatureInfo);
        }
        }


        return new VerifiedSigner(
        return new VerifiedSigner(
+4 −2
Original line number Original line Diff line number Diff line
@@ -165,7 +165,7 @@ public class ApkSignatureSchemeV3Verifier {
    private static VerifiedSigner verify(
    private static VerifiedSigner verify(
            RandomAccessFile apk,
            RandomAccessFile apk,
            SignatureInfo signatureInfo,
            SignatureInfo signatureInfo,
            boolean doVerifyIntegrity) throws SecurityException {
            boolean doVerifyIntegrity) throws SecurityException, IOException {
        int signerCount = 0;
        int signerCount = 0;
        Map<Integer, byte[]> contentDigests = new ArrayMap<>();
        Map<Integer, byte[]> contentDigests = new ArrayMap<>();
        VerifiedSigner result = null;
        VerifiedSigner result = null;
@@ -214,7 +214,9 @@ public class ApkSignatureSchemeV3Verifier {
        }
        }


        if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
        if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
            result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
            byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
            result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
                    verityDigest, apk.length(), signatureInfo);
        }
        }


        return result;
        return result;
+39 −4
Original line number Original line Diff line number Diff line
@@ -285,11 +285,46 @@ final class ApkSigningBlockUtils {
        return result;
        return result;
    }
    }


    /**
     * Return the verity digest only if the length of digest content looks correct.
     * When verity digest is generated, the last incomplete 4k chunk is padded with 0s before
     * hashing. This means two almost identical APKs with different number of 0 at the end will have
     * the same verity digest. To avoid this problem, the length of the source content (excluding
     * Signing Block) is appended to the verity digest, and the digest is returned only if the
     * length is consistent to the current APK.
     */
    static byte[] parseVerityDigestAndVerifySourceLength(
            byte[] data, long fileSize, SignatureInfo signatureInfo) throws SecurityException {
        // FORMAT:
        // OFFSET       DATA TYPE  DESCRIPTION
        // * @+0  bytes uint8[32]  Merkle tree root hash of SHA-256
        // * @+32 bytes int64      Length of source data
        int kRootHashSize = 32;
        int kSourceLengthSize = 8;

        if (data.length != kRootHashSize + kSourceLengthSize) {
            throw new SecurityException("Verity digest size is wrong: " + data.length);
        }
        ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
        buffer.position(kRootHashSize);
        long expectedSourceLength = buffer.getLong();

        long signingBlockSize = signatureInfo.centralDirOffset
                - signatureInfo.apkSigningBlockOffset;
        if (expectedSourceLength != fileSize - signingBlockSize) {
            throw new SecurityException("APK content size did not verify");
        }

        return Arrays.copyOfRange(data, 0, kRootHashSize);
    }

    private static void verifyIntegrityForVerityBasedAlgorithm(
    private static void verifyIntegrityForVerityBasedAlgorithm(
            byte[] expectedRootHash,
            byte[] expectedDigest,
            RandomAccessFile apk,
            RandomAccessFile apk,
            SignatureInfo signatureInfo) throws SecurityException {
            SignatureInfo signatureInfo) throws SecurityException {
        try {
        try {
            byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
                    apk.length(), signatureInfo);
            ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
            ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
                    signatureInfo, new ByteBufferFactory() {
                    signatureInfo, new ByteBufferFactory() {
                        @Override
                        @Override
@@ -373,9 +408,9 @@ final class ApkSigningBlockUtils {
    static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
    static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
    static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
    static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
    static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
    static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
    static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0411;
    static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0421;
    static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0413;
    static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423;
    static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0415;
    static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425;


    static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
    static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
    static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
    static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;