Loading core/java/android/util/apk/ApkVerityBuilder.java +35 −25 Original line number Diff line number Diff line Loading @@ -72,22 +72,31 @@ abstract class ApkVerityBuilder { signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; long dataSize = apk.length() - signingBlockSize; int[] levelOffset = calculateVerityLevelOffset(dataSize); int merkleTreeSize = levelOffset[levelOffset.length - 1]; ByteBuffer output = bufferFactory.create( CHUNK_SIZE_BYTES + // fsverity header + extensions + padding levelOffset[levelOffset.length - 1]); // Merkle tree size merkleTreeSize + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata output.order(ByteOrder.LITTLE_ENDIAN); ByteBuffer header = slice(output, 0, FSVERITY_HEADER_SIZE_BYTES); ByteBuffer extensions = slice(output, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); ByteBuffer tree = slice(output, CHUNK_SIZE_BYTES, output.limit()); ByteBuffer tree = slice(output, 0, merkleTreeSize); ByteBuffer header = slice(output, merkleTreeSize, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES); ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES, merkleTreeSize + CHUNK_SIZE_BYTES); byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES]; ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes); apkDigest.order(ByteOrder.LITTLE_ENDIAN); // NB: Buffer limit is set inside once finished. calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions); output.rewind(); // Put the reverse offset to fs-verity header at the end. output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit()); output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit() + 4); // size of this integer right before EOF output.flip(); return new ApkVerityResult(output, apkDigestBytes); } Loading @@ -101,7 +110,8 @@ abstract class ApkVerityBuilder { ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES) .order(ByteOrder.LITTLE_ENDIAN); ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES); ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES); calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions); Loading Loading @@ -328,10 +338,10 @@ abstract class ApkVerityBuilder { buffer.put((byte) 12); // log2(block-size): log2(4096) buffer.put((byte) 7); // log2(leaves-per-node): log2(4096 / 32) buffer.putShort((short) 1); // meta algorithm, SHA256_MODE == 1 buffer.putShort((short) 1); // data algorithm, SHA256_MODE == 1 buffer.putShort((short) 1); // meta algorithm, SHA256 == 1 buffer.putShort((short) 1); // data algorithm, SHA256 == 1 buffer.putInt(0x0); // flags buffer.putInt(0); // flags buffer.putInt(0); // reserved buffer.putLong(fileSize); // original file size Loading Loading @@ -362,12 +372,11 @@ abstract class ApkVerityBuilder { // // struct fsverity_extension_patch { // __le64 offset; // u8 length; // u8 reserved[7]; // u8 databytes[]; // }; final int kSizeOfFsverityExtensionHeader = 8; final int kExtensionSizeAlignment = 8; { // struct fsverity_extension #1 Loading @@ -385,23 +394,24 @@ abstract class ApkVerityBuilder { { // struct fsverity_extension #2 final int kSizeOfFsverityPatchExtension = 8 + // offset size 1 + // size of length from offset (up to 255) 7 + // reserved ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; final int kPadding = (int) divideRoundup(kSizeOfFsverityPatchExtension % 8, 8); final int kTotalSize = kSizeOfFsverityExtensionHeader + 8 // offset size + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; buffer.putShort((short) // total size of extension, padded to 64-bit alignment (kSizeOfFsverityExtensionHeader + kSizeOfFsverityPatchExtension + kPadding)); buffer.putShort((short) kTotalSize); buffer.put((byte) 1); // ID of patch extension skip(buffer, 5); // reserved // struct fsverity_extension_patch buffer.putLong(eocdOffset); // offset buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE); // length skip(buffer, 7); // reserved buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET); // offset buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes // The extension needs to be 0-padded at the end, since the length may not be multiple // of 8. int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment; if (kPadding == kExtensionSizeAlignment) { kPadding = 0; } skip(buffer, kPadding); // padding } Loading services/core/java/com/android/server/pm/Installer.java +2 −2 Original line number Diff line number Diff line Loading @@ -484,11 +484,11 @@ public class Installer extends SystemService { } } public void installApkVerity(String filePath, FileDescriptor verityInput) public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize) throws InstallerException { if (!checkBeforeRemote()) return; try { mInstalld.installApkVerity(filePath, verityInput); mInstalld.installApkVerity(filePath, verityInput, contentSize); } catch (Exception e) { throw InstallerException.from(e); } Loading services/core/java/com/android/server/pm/PackageManagerService.java +5 −2 Original line number Diff line number Diff line Loading @@ -17322,8 +17322,11 @@ public class PackageManagerService extends IPackageManager.Stub if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath); FileDescriptor fd = result.getUnownedFileDescriptor(); try { mInstaller.installApkVerity(apkPath, fd); } catch (InstallerException e) { final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath); mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to set up verity: " + e); return; services/core/java/com/android/server/security/VerityUtils.java +36 −11 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.system.Os; import android.util.apk.ApkSignatureVerifier; import android.util.apk.ByteBufferFactory; import android.util.apk.SignatureNotFoundException; import android.util.Pair; import android.util.Slog; import java.io.FileDescriptor; Loading Loading @@ -59,12 +60,15 @@ abstract public class VerityUtils { return SetupResult.skipped(); } shm = generateApkVerityIntoSharedMemory(apkPath, signedRootHash); Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath, signedRootHash); shm = result.first; int contentSize = result.second; FileDescriptor rfd = shm.getFileDescriptor(); if (rfd == null || !rfd.valid()) { return SetupResult.failed(); } return SetupResult.ok(Os.dup(rfd)); return SetupResult.ok(Os.dup(rfd), contentSize); } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException | SignatureNotFoundException | ErrnoException e) { Slog.e(TAG, "Failed to set up apk verity: ", e); Loading @@ -85,10 +89,20 @@ abstract public class VerityUtils { } /** * Returns a {@code SharedMemory} that contains Merkle tree and fsverity headers for the given * apk, in the form that can immediately be used for fsverity setup. * {@see ApkSignatureVerifier#getVerityRootHash(String)}. */ private static SharedMemory generateApkVerityIntoSharedMemory( public static byte[] getVerityRootHash(@NonNull String apkPath) throws IOException, SignatureNotFoundException, SecurityException { return ApkSignatureVerifier.getVerityRootHash(apkPath); } /** * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has * length equals to the returned {@code Integer}. */ private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory( String apkPath, byte[] expectedRootHash) throws IOException, SecurityException, DigestException, NoSuchAlgorithmException, SignatureNotFoundException { Loading @@ -101,6 +115,7 @@ abstract public class VerityUtils { throw new SecurityException("Locally generated verity root hash does not match"); } int contentSize = shmBufferFactory.getBufferLimit(); SharedMemory shm = shmBufferFactory.releaseSharedMemory(); if (shm == null) { throw new IllegalStateException("Failed to generate verity tree into shared memory"); Loading @@ -108,7 +123,7 @@ abstract public class VerityUtils { if (!shm.setProtect(PROT_READ)) { throw new SecurityException("Failed to set up shared memory correctly"); } return shm; return Pair.create(shm, contentSize); } public static class SetupResult { Loading @@ -123,22 +138,24 @@ abstract public class VerityUtils { private final int mCode; private final FileDescriptor mFileDescriptor; private final int mContentSize; public static SetupResult ok(@NonNull FileDescriptor fileDescriptor) { return new SetupResult(RESULT_OK, fileDescriptor); public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) { return new SetupResult(RESULT_OK, fileDescriptor, contentSize); } public static SetupResult skipped() { return new SetupResult(RESULT_SKIPPED, null); return new SetupResult(RESULT_SKIPPED, null, -1); } public static SetupResult failed() { return new SetupResult(RESULT_FAILED, null); return new SetupResult(RESULT_FAILED, null, -1); } private SetupResult(int code, FileDescriptor fileDescriptor) { private SetupResult(int code, FileDescriptor fileDescriptor, int contentSize) { this.mCode = code; this.mFileDescriptor = fileDescriptor; this.mContentSize = contentSize; } public boolean isFailed() { Loading @@ -152,6 +169,10 @@ abstract public class VerityUtils { public @NonNull FileDescriptor getUnownedFileDescriptor() { return mFileDescriptor; } public int getContentSize() { return mContentSize; } } /** A {@code ByteBufferFactory} that creates a shared memory backed {@code ByteBuffer}. */ Loading Loading @@ -188,5 +209,9 @@ abstract public class VerityUtils { mShm = null; return tmp; } public int getBufferLimit() { return mBuffer == null ? -1 : mBuffer.limit(); } } } Loading
core/java/android/util/apk/ApkVerityBuilder.java +35 −25 Original line number Diff line number Diff line Loading @@ -72,22 +72,31 @@ abstract class ApkVerityBuilder { signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; long dataSize = apk.length() - signingBlockSize; int[] levelOffset = calculateVerityLevelOffset(dataSize); int merkleTreeSize = levelOffset[levelOffset.length - 1]; ByteBuffer output = bufferFactory.create( CHUNK_SIZE_BYTES + // fsverity header + extensions + padding levelOffset[levelOffset.length - 1]); // Merkle tree size merkleTreeSize + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata output.order(ByteOrder.LITTLE_ENDIAN); ByteBuffer header = slice(output, 0, FSVERITY_HEADER_SIZE_BYTES); ByteBuffer extensions = slice(output, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); ByteBuffer tree = slice(output, CHUNK_SIZE_BYTES, output.limit()); ByteBuffer tree = slice(output, 0, merkleTreeSize); ByteBuffer header = slice(output, merkleTreeSize, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES); ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES, merkleTreeSize + CHUNK_SIZE_BYTES); byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES]; ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes); apkDigest.order(ByteOrder.LITTLE_ENDIAN); // NB: Buffer limit is set inside once finished. calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions); output.rewind(); // Put the reverse offset to fs-verity header at the end. output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit()); output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit() + 4); // size of this integer right before EOF output.flip(); return new ApkVerityResult(output, apkDigestBytes); } Loading @@ -101,7 +110,8 @@ abstract class ApkVerityBuilder { ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES) .order(ByteOrder.LITTLE_ENDIAN); ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES); ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES); calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions); Loading Loading @@ -328,10 +338,10 @@ abstract class ApkVerityBuilder { buffer.put((byte) 12); // log2(block-size): log2(4096) buffer.put((byte) 7); // log2(leaves-per-node): log2(4096 / 32) buffer.putShort((short) 1); // meta algorithm, SHA256_MODE == 1 buffer.putShort((short) 1); // data algorithm, SHA256_MODE == 1 buffer.putShort((short) 1); // meta algorithm, SHA256 == 1 buffer.putShort((short) 1); // data algorithm, SHA256 == 1 buffer.putInt(0x0); // flags buffer.putInt(0); // flags buffer.putInt(0); // reserved buffer.putLong(fileSize); // original file size Loading Loading @@ -362,12 +372,11 @@ abstract class ApkVerityBuilder { // // struct fsverity_extension_patch { // __le64 offset; // u8 length; // u8 reserved[7]; // u8 databytes[]; // }; final int kSizeOfFsverityExtensionHeader = 8; final int kExtensionSizeAlignment = 8; { // struct fsverity_extension #1 Loading @@ -385,23 +394,24 @@ abstract class ApkVerityBuilder { { // struct fsverity_extension #2 final int kSizeOfFsverityPatchExtension = 8 + // offset size 1 + // size of length from offset (up to 255) 7 + // reserved ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; final int kPadding = (int) divideRoundup(kSizeOfFsverityPatchExtension % 8, 8); final int kTotalSize = kSizeOfFsverityExtensionHeader + 8 // offset size + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; buffer.putShort((short) // total size of extension, padded to 64-bit alignment (kSizeOfFsverityExtensionHeader + kSizeOfFsverityPatchExtension + kPadding)); buffer.putShort((short) kTotalSize); buffer.put((byte) 1); // ID of patch extension skip(buffer, 5); // reserved // struct fsverity_extension_patch buffer.putLong(eocdOffset); // offset buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE); // length skip(buffer, 7); // reserved buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET); // offset buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes // The extension needs to be 0-padded at the end, since the length may not be multiple // of 8. int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment; if (kPadding == kExtensionSizeAlignment) { kPadding = 0; } skip(buffer, kPadding); // padding } Loading
services/core/java/com/android/server/pm/Installer.java +2 −2 Original line number Diff line number Diff line Loading @@ -484,11 +484,11 @@ public class Installer extends SystemService { } } public void installApkVerity(String filePath, FileDescriptor verityInput) public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize) throws InstallerException { if (!checkBeforeRemote()) return; try { mInstalld.installApkVerity(filePath, verityInput); mInstalld.installApkVerity(filePath, verityInput, contentSize); } catch (Exception e) { throw InstallerException.from(e); } Loading
services/core/java/com/android/server/pm/PackageManagerService.java +5 −2 Original line number Diff line number Diff line Loading @@ -17322,8 +17322,11 @@ public class PackageManagerService extends IPackageManager.Stub if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath); FileDescriptor fd = result.getUnownedFileDescriptor(); try { mInstaller.installApkVerity(apkPath, fd); } catch (InstallerException e) { final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath); mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to set up verity: " + e); return;
services/core/java/com/android/server/security/VerityUtils.java +36 −11 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.system.Os; import android.util.apk.ApkSignatureVerifier; import android.util.apk.ByteBufferFactory; import android.util.apk.SignatureNotFoundException; import android.util.Pair; import android.util.Slog; import java.io.FileDescriptor; Loading Loading @@ -59,12 +60,15 @@ abstract public class VerityUtils { return SetupResult.skipped(); } shm = generateApkVerityIntoSharedMemory(apkPath, signedRootHash); Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath, signedRootHash); shm = result.first; int contentSize = result.second; FileDescriptor rfd = shm.getFileDescriptor(); if (rfd == null || !rfd.valid()) { return SetupResult.failed(); } return SetupResult.ok(Os.dup(rfd)); return SetupResult.ok(Os.dup(rfd), contentSize); } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException | SignatureNotFoundException | ErrnoException e) { Slog.e(TAG, "Failed to set up apk verity: ", e); Loading @@ -85,10 +89,20 @@ abstract public class VerityUtils { } /** * Returns a {@code SharedMemory} that contains Merkle tree and fsverity headers for the given * apk, in the form that can immediately be used for fsverity setup. * {@see ApkSignatureVerifier#getVerityRootHash(String)}. */ private static SharedMemory generateApkVerityIntoSharedMemory( public static byte[] getVerityRootHash(@NonNull String apkPath) throws IOException, SignatureNotFoundException, SecurityException { return ApkSignatureVerifier.getVerityRootHash(apkPath); } /** * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has * length equals to the returned {@code Integer}. */ private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory( String apkPath, byte[] expectedRootHash) throws IOException, SecurityException, DigestException, NoSuchAlgorithmException, SignatureNotFoundException { Loading @@ -101,6 +115,7 @@ abstract public class VerityUtils { throw new SecurityException("Locally generated verity root hash does not match"); } int contentSize = shmBufferFactory.getBufferLimit(); SharedMemory shm = shmBufferFactory.releaseSharedMemory(); if (shm == null) { throw new IllegalStateException("Failed to generate verity tree into shared memory"); Loading @@ -108,7 +123,7 @@ abstract public class VerityUtils { if (!shm.setProtect(PROT_READ)) { throw new SecurityException("Failed to set up shared memory correctly"); } return shm; return Pair.create(shm, contentSize); } public static class SetupResult { Loading @@ -123,22 +138,24 @@ abstract public class VerityUtils { private final int mCode; private final FileDescriptor mFileDescriptor; private final int mContentSize; public static SetupResult ok(@NonNull FileDescriptor fileDescriptor) { return new SetupResult(RESULT_OK, fileDescriptor); public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) { return new SetupResult(RESULT_OK, fileDescriptor, contentSize); } public static SetupResult skipped() { return new SetupResult(RESULT_SKIPPED, null); return new SetupResult(RESULT_SKIPPED, null, -1); } public static SetupResult failed() { return new SetupResult(RESULT_FAILED, null); return new SetupResult(RESULT_FAILED, null, -1); } private SetupResult(int code, FileDescriptor fileDescriptor) { private SetupResult(int code, FileDescriptor fileDescriptor, int contentSize) { this.mCode = code; this.mFileDescriptor = fileDescriptor; this.mContentSize = contentSize; } public boolean isFailed() { Loading @@ -152,6 +169,10 @@ abstract public class VerityUtils { public @NonNull FileDescriptor getUnownedFileDescriptor() { return mFileDescriptor; } public int getContentSize() { return mContentSize; } } /** A {@code ByteBufferFactory} that creates a shared memory backed {@code ByteBuffer}. */ Loading Loading @@ -188,5 +209,9 @@ abstract public class VerityUtils { mShm = null; return tmp; } public int getBufferLimit() { return mBuffer == null ? -1 : mBuffer.limit(); } } }