Merge "Fix fs-verity per fs-verity spec change" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
01a0288ca1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -385,24 +394,25 @@ 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.putInt(Math.toIntExact(signingBlockOffset)); // databytes
|
||||
skip(buffer, kPadding); // padding
|
||||
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
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
@@ -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");
|
||||
@@ -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 {
|
||||
@@ -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() {
|
||||
@@ -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}. */
|
||||
@@ -188,5 +209,9 @@ abstract public class VerityUtils {
|
||||
mShm = null;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public int getBufferLimit() {
|
||||
return mBuffer == null ? -1 : mBuffer.limit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user