Merge "Fix fs-verity per fs-verity spec change" into pi-dev

am: 01a0288ca1

Change-Id: I4d7aade7fe59008cd31073584fc0973f0ebc234a
This commit is contained in:
Victor Hsieh
2018-03-30 21:08:07 +00:00
committed by android-build-merger
4 changed files with 78 additions and 40 deletions

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -17333,8 +17333,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;

View File

@@ -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();
}
}
}