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

Commit 27300925 authored by Victor Hsieh's avatar Victor Hsieh
Browse files

Disambiguate fs-verity and apk-verity

Standard fs-verity does not allow skipping part of the file.  Let's call
the original version for APK "apk-verity" for now.

Test: atest PtsApkVerityTestCases
Bug: 112037636
Change-Id: I11264d5959b034fe373802c53d08f329fa926e58
parent 6bf1126c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -410,7 +410,7 @@ public class ApkSignatureSchemeV2Verifier {
                   NoSuchAlgorithmException {
        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
            SignatureInfo signatureInfo = findSignature(apk);
            return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
            return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
        }
    }

@@ -423,7 +423,7 @@ public class ApkSignatureSchemeV2Verifier {
            if (vSigner.verityRootHash == null) {
                return null;
            }
            return ApkVerityBuilder.generateApkVerityRootHash(
            return VerityBuilder.generateApkVerityRootHash(
                    apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
        }
    }
+2 −2
Original line number Diff line number Diff line
@@ -534,7 +534,7 @@ public class ApkSignatureSchemeV3Verifier {
                   NoSuchAlgorithmException {
        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
            SignatureInfo signatureInfo = findSignature(apk);
            return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
            return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
        }
    }

@@ -547,7 +547,7 @@ public class ApkSignatureSchemeV3Verifier {
            if (vSigner.verityRootHash == null) {
                return null;
            }
            return ApkVerityBuilder.generateApkVerityRootHash(
            return VerityBuilder.generateApkVerityRootHash(
                    apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -332,7 +332,7 @@ final class ApkSigningBlockUtils {
        try {
            byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
                    apk.length(), signatureInfo);
            ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk,
            VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk,
                    signatureInfo, new ByteBufferFactory() {
                        @Override
                        public ByteBuffer create(int capacity) {
+20 −20
Original line number Diff line number Diff line
@@ -29,19 +29,18 @@ import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

/**
 * ApkVerityBuilder builds the APK verity tree and the verity header.  The generated tree format can
 * be stored on disk for apk-verity setup and used by kernel.  Note that since the current
 * implementation is different from the upstream, we call this implementation apk-verity instead of
 * fs-verity.
 * VerityBuilder builds the verity Merkle tree and other metadata.  The generated tree format can
 * be stored on disk for fs-verity setup and used by kernel.  The builder support standard
 * fs-verity, and Android specific apk-verity that requires additional kernel patches.
 *
 * <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to
 * the existing APK format, it has to skip APK Signing Block and also has some special treatment for
 * the "Central Directory offset" field of ZIP End of Central Directory.
 * <p>Unlike a regular Merkle tree of fs-verity, the apk-verity tree does not cover the file content
 * fully, and has to skip APK Signing Block with some special treatment for the "Central Directory
 * offset" field of ZIP End of Central Directory.
 *
 * @hide
 */
public abstract class ApkVerityBuilder {
    private ApkVerityBuilder() {}
public abstract class VerityBuilder {
    private VerityBuilder() {}

    private static final int CHUNK_SIZE_BYTES = 4096;  // Typical Linux block size
    private static final int DIGEST_SIZE_BYTES = 32;  // SHA-256 size
@@ -52,7 +51,7 @@ public abstract class ApkVerityBuilder {
    private static final byte[] DEFAULT_SALT = new byte[8];

    /** Result generated by the builder. */
    public static class ApkVerityResult {
    public static class VerityResult {
        /** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */
        public final ByteBuffer verityData;

@@ -62,7 +61,7 @@ public abstract class ApkVerityBuilder {
        /** Root hash of the Merkle tree. */
        public final byte[] rootHash;

        private ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
        private VerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
            this.verityData = verityData;
            this.merkleTreeSize = merkleTreeSize;
            this.rootHash = rootHash;
@@ -74,14 +73,14 @@ public abstract class ApkVerityBuilder {
     * ByteBuffer} created by the {@link ByteBufferFactory}.  The output is suitable to be used as
     * the on-disk format for fs-verity to use.
     *
     * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
     * @return VerityResult containing a buffer with the generated Merkle tree stored at the
     *         front, the tree size, and the calculated root hash.
     */
    @NonNull
    public static ApkVerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
    public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
            @NonNull ByteBufferFactory bufferFactory)
            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
        return generateVerityTree(apk, bufferFactory, null /* signatureInfo */,
        return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */,
                false /* skipSigningBlock */);
    }

@@ -91,18 +90,19 @@ public abstract class ApkVerityBuilder {
     * Block specificed in {@code signatureInfo}.  The output is suitable to be used as the on-disk
     * format for fs-verity to use (with elide and patch extensions).
     *
     * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
     * @return VerityResult containing a buffer with the generated Merkle tree stored at the
     *         front, the tree size, and the calculated root hash.
     */
    @NonNull
    public static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
    public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
            @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
        return generateVerityTree(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */);
        return generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
                true /* skipSigningBlock */);
    }

    @NonNull
    private static ApkVerityResult generateVerityTree(@NonNull RandomAccessFile apk,
    private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk,
            @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
            boolean skipSigningBlock)
            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
@@ -124,7 +124,7 @@ public abstract class ApkVerityBuilder {
        byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
        byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
                tree, skipSigningBlock);
        return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
        return new VerityResult(output, merkleTreeSize, apkRootHash);
    }

    static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
@@ -173,7 +173,7 @@ public abstract class ApkVerityBuilder {
            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
                   NoSuchAlgorithmException {
        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
            ApkVerityResult result = generateVerityTree(apk, bufferFactory, signatureInfo,
            VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
                    true /* skipSigningBlock */);
            ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
                    result.verityData.limit());
+3 −2
Original line number Diff line number Diff line
@@ -8474,7 +8474,7 @@ public class PackageManagerService extends IPackageManager.Stub
    private boolean canSkipFullApkVerification(String apkPath) {
        final byte[] rootHashObserved;
        try {
            rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
            rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
            if (rootHashObserved == null) {
                return false;  // APK does not contain Merkle tree root hash.
            }
@@ -16010,7 +16010,8 @@ public class PackageManagerService extends IPackageManager.Stub
                    if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
                    FileDescriptor fd = result.getUnownedFileDescriptor();
                    try {
                        final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);
                        final byte[] signedRootHash =
                                VerityUtils.generateApkVerityRootHash(apkPath);
                        mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
                        mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
                    } catch (InstallerException | IOException | DigestException |
Loading