Merge "Change fs-verity setup to the new format"
am: a825bc7ed3
Change-Id: If80778ec120cd506b65770241ee2272fc5c6615b
This commit is contained in:
@@ -17474,8 +17474,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
|
if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
|
||||||
try {
|
try {
|
||||||
VerityUtils.setUpFsverity(filePath, signaturePath);
|
VerityUtils.setUpFsverity(filePath, signaturePath);
|
||||||
} catch (IOException | DigestException | NoSuchAlgorithmException
|
} catch (IOException e) {
|
||||||
| SecurityException e) {
|
|
||||||
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
|
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
|
||||||
"Failed to enable fs-verity: " + e);
|
"Failed to enable fs-verity: " + e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,27 +26,19 @@ import android.util.Slog;
|
|||||||
import android.util.apk.ApkSignatureVerifier;
|
import android.util.apk.ApkSignatureVerifier;
|
||||||
import android.util.apk.ByteBufferFactory;
|
import android.util.apk.ByteBufferFactory;
|
||||||
import android.util.apk.SignatureNotFoundException;
|
import android.util.apk.SignatureNotFoundException;
|
||||||
import android.util.apk.VerityBuilder;
|
|
||||||
|
|
||||||
import libcore.util.HexEncoding;
|
import libcore.util.HexEncoding;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.DigestException;
|
import java.security.DigestException;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import sun.security.pkcs.PKCS7;
|
|
||||||
|
|
||||||
/** Provides fsverity related operations. */
|
/** Provides fsverity related operations. */
|
||||||
abstract public class VerityUtils {
|
abstract public class VerityUtils {
|
||||||
private static final String TAG = "VerityUtils";
|
private static final String TAG = "VerityUtils";
|
||||||
@@ -60,8 +52,6 @@ abstract public class VerityUtils {
|
|||||||
/** The maximum size of signature file. This is just to avoid potential abuse. */
|
/** The maximum size of signature file. This is just to avoid potential abuse. */
|
||||||
private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
|
private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
|
||||||
|
|
||||||
private static final int COMMON_LINUX_PAGE_SIZE_IN_BYTES = 4096;
|
|
||||||
|
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
/** Returns true if the given file looks like containing an fs-verity signature. */
|
/** Returns true if the given file looks like containing an fs-verity signature. */
|
||||||
@@ -74,42 +64,15 @@ abstract public class VerityUtils {
|
|||||||
return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
|
return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generates Merkle tree and fs-verity metadata then enables fs-verity. */
|
/** Enables fs-verity for the file with a PKCS#7 detached signature file. */
|
||||||
public static void setUpFsverity(@NonNull String filePath, String signaturePath)
|
public static void setUpFsverity(@NonNull String filePath, @NonNull String signaturePath)
|
||||||
throws IOException, DigestException, NoSuchAlgorithmException {
|
throws IOException {
|
||||||
final PKCS7 pkcs7 = new PKCS7(Files.readAllBytes(Paths.get(signaturePath)));
|
if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
|
||||||
final byte[] expectedMeasurement = pkcs7.getContentInfo().getContentBytes();
|
throw new SecurityException("Signature file is unexpectedly large: " + signaturePath);
|
||||||
if (DEBUG) {
|
|
||||||
Slog.d(TAG, "Enabling fs-verity with signed fs-verity measurement "
|
|
||||||
+ bytesToString(expectedMeasurement));
|
|
||||||
Slog.d(TAG, "PKCS#7 info: " + pkcs7);
|
|
||||||
}
|
}
|
||||||
|
byte[] pkcs7Signature = Files.readAllBytes(Paths.get(signaturePath));
|
||||||
final TrackedBufferFactory bufferFactory = new TrackedBufferFactory();
|
// This will fail if the public key is not already in .fs-verity kernel keyring.
|
||||||
final byte[] actualMeasurement = generateFsverityMetadata(filePath, signaturePath,
|
int errno = enableFsverityNative(filePath, pkcs7Signature);
|
||||||
bufferFactory);
|
|
||||||
try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw")) {
|
|
||||||
FileChannel ch = raf.getChannel();
|
|
||||||
ch.position(roundUpToNextMultiple(ch.size(), COMMON_LINUX_PAGE_SIZE_IN_BYTES));
|
|
||||||
ByteBuffer buffer = bufferFactory.getBuffer();
|
|
||||||
|
|
||||||
long offset = buffer.position();
|
|
||||||
long size = buffer.limit();
|
|
||||||
while (offset < size) {
|
|
||||||
long s = ch.write(buffer);
|
|
||||||
offset += s;
|
|
||||||
size -= s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Arrays.equals(expectedMeasurement, actualMeasurement)) {
|
|
||||||
throw new SecurityException("fs-verity measurement mismatch: "
|
|
||||||
+ bytesToString(actualMeasurement) + " != "
|
|
||||||
+ bytesToString(expectedMeasurement));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This can fail if the public key is not already in .fs-verity kernel keyring.
|
|
||||||
int errno = enableFsverityNative(filePath);
|
|
||||||
if (errno != 0) {
|
if (errno != 0) {
|
||||||
throw new IOException("Failed to enable fs-verity on " + filePath + ": "
|
throw new IOException("Failed to enable fs-verity on " + filePath + ": "
|
||||||
+ Os.strerror(errno));
|
+ Os.strerror(errno));
|
||||||
@@ -131,12 +94,19 @@ abstract public class VerityUtils {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static native int enableFsverityNative(@NonNull String filePath,
|
||||||
|
@NonNull byte[] pkcs7Signature);
|
||||||
|
private static native int measureFsverityNative(@NonNull String filePath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
|
* Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
|
||||||
*
|
*
|
||||||
|
* @deprecated This is only used for previous fs-verity implementation, and should never be used
|
||||||
|
* on new devices.
|
||||||
* @return {@code SetupResult} that contains the result code, and when success, the
|
* @return {@code SetupResult} that contains the result code, and when success, the
|
||||||
* {@code FileDescriptor} to read all the data from.
|
* {@code FileDescriptor} to read all the data from.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) {
|
public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Trying to install legacy apk verity to " + apkPath);
|
Slog.d(TAG, "Trying to install legacy apk verity to " + apkPath);
|
||||||
@@ -173,7 +143,10 @@ abstract public class VerityUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}.
|
* {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}.
|
||||||
|
* @deprecated This is only used for previous fs-verity implementation, and should never be used
|
||||||
|
* on new devices.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static byte[] generateApkVerityRootHash(@NonNull String apkPath)
|
public static byte[] generateApkVerityRootHash(@NonNull String apkPath)
|
||||||
throws NoSuchAlgorithmException, DigestException, IOException {
|
throws NoSuchAlgorithmException, DigestException, IOException {
|
||||||
return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
|
return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
|
||||||
@@ -181,103 +154,15 @@ abstract public class VerityUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@see ApkSignatureVerifier#getVerityRootHash(String)}.
|
* {@see ApkSignatureVerifier#getVerityRootHash(String)}.
|
||||||
|
* @deprecated This is only used for previous fs-verity implementation, and should never be used
|
||||||
|
* on new devices.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static byte[] getVerityRootHash(@NonNull String apkPath)
|
public static byte[] getVerityRootHash(@NonNull String apkPath)
|
||||||
throws IOException, SignatureNotFoundException {
|
throws IOException, SignatureNotFoundException {
|
||||||
return ApkSignatureVerifier.getVerityRootHash(apkPath);
|
return ApkSignatureVerifier.getVerityRootHash(apkPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates fs-verity metadata for {@code filePath} in the buffer created by {@code
|
|
||||||
* trackedBufferFactory}. The metadata contains the Merkle tree, fs-verity descriptor and
|
|
||||||
* extensions, including a PKCS#7 signature provided in {@code signaturePath}.
|
|
||||||
*
|
|
||||||
* <p>It is worthy to note that {@code trackedBufferFactory} generates a "tracked" {@code
|
|
||||||
* ByteBuffer}. The data will be used outside this method via the factory itself.
|
|
||||||
*
|
|
||||||
* @return fs-verity signed data (struct fsverity_digest_disk) of {@code filePath}, which
|
|
||||||
* includes SHA-256 of fs-verity descriptor and authenticated extensions.
|
|
||||||
*/
|
|
||||||
private static byte[] generateFsverityMetadata(String filePath, String signaturePath,
|
|
||||||
@NonNull ByteBufferFactory trackedBufferFactory)
|
|
||||||
throws IOException, DigestException, NoSuchAlgorithmException {
|
|
||||||
try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
|
|
||||||
VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree(
|
|
||||||
file, trackedBufferFactory);
|
|
||||||
|
|
||||||
ByteBuffer buffer = result.verityData;
|
|
||||||
buffer.position(result.merkleTreeSize);
|
|
||||||
|
|
||||||
final byte[] measurement = generateFsverityDescriptorAndMeasurement(file,
|
|
||||||
result.rootHash, signaturePath, buffer);
|
|
||||||
buffer.flip();
|
|
||||||
return constructFsveritySignedDataNative(measurement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates fs-verity descriptor including the extensions to the {@code output} and returns the
|
|
||||||
* fs-verity measurement.
|
|
||||||
*
|
|
||||||
* @return fs-verity measurement, which is a SHA-256 of fs-verity descriptor and authenticated
|
|
||||||
* extensions.
|
|
||||||
*/
|
|
||||||
private static byte[] generateFsverityDescriptorAndMeasurement(
|
|
||||||
@NonNull RandomAccessFile file, @NonNull byte[] rootHash,
|
|
||||||
@NonNull String pkcs7SignaturePath, @NonNull ByteBuffer output)
|
|
||||||
throws IOException, NoSuchAlgorithmException, DigestException {
|
|
||||||
final short kRootHashExtensionId = 1;
|
|
||||||
final short kPkcs7SignatureExtensionId = 3;
|
|
||||||
final int origPosition = output.position();
|
|
||||||
|
|
||||||
// For generating fs-verity file measurement, which consists of the descriptor and
|
|
||||||
// authenticated extensions (but not unauthenticated extensions and the footer).
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
||||||
|
|
||||||
// 1. Generate fs-verity descriptor.
|
|
||||||
final byte[] desc = constructFsverityDescriptorNative(file.length());
|
|
||||||
output.put(desc);
|
|
||||||
md.update(desc);
|
|
||||||
|
|
||||||
// 2. Generate authenticated extensions.
|
|
||||||
final byte[] authExt =
|
|
||||||
constructFsverityExtensionNative(kRootHashExtensionId, rootHash.length);
|
|
||||||
output.put(authExt);
|
|
||||||
output.put(rootHash);
|
|
||||||
md.update(authExt);
|
|
||||||
md.update(rootHash);
|
|
||||||
|
|
||||||
// 3. Generate unauthenticated extensions.
|
|
||||||
ByteBuffer header = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
output.putShort((short) 1); // number of unauthenticated extensions below
|
|
||||||
output.position(output.position() + 6);
|
|
||||||
|
|
||||||
// Generate PKCS#7 extension. NB: We do not verify agaist trusted certificate (should be
|
|
||||||
// done by the caller if needed).
|
|
||||||
Path path = Paths.get(pkcs7SignaturePath);
|
|
||||||
if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
|
|
||||||
throw new IllegalArgumentException("Signature size is unexpectedly large: "
|
|
||||||
+ pkcs7SignaturePath);
|
|
||||||
}
|
|
||||||
final byte[] pkcs7Signature = Files.readAllBytes(path);
|
|
||||||
output.put(constructFsverityExtensionNative(kPkcs7SignatureExtensionId,
|
|
||||||
pkcs7Signature.length));
|
|
||||||
output.put(pkcs7Signature);
|
|
||||||
|
|
||||||
// 4. Generate the footer.
|
|
||||||
output.put(constructFsverityFooterNative(output.position() - origPosition));
|
|
||||||
|
|
||||||
return md.digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native int enableFsverityNative(@NonNull String filePath);
|
|
||||||
private static native int measureFsverityNative(@NonNull String filePath);
|
|
||||||
private static native byte[] constructFsveritySignedDataNative(@NonNull byte[] measurement);
|
|
||||||
private static native byte[] constructFsverityDescriptorNative(long fileSize);
|
|
||||||
private static native byte[] constructFsverityExtensionNative(short extensionId,
|
|
||||||
int extensionDataSize);
|
|
||||||
private static native byte[] constructFsverityFooterNative(int offsetToDescriptorHead);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains
|
* 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
|
* Merkle tree and fsverity headers for the given apk, in the form that can immediately be used
|
||||||
@@ -313,6 +198,11 @@ abstract public class VerityUtils {
|
|||||||
return HexEncoding.encodeToString(bytes);
|
return HexEncoding.encodeToString(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This is only used for previous fs-verity implementation, and should never be used
|
||||||
|
* on new devices.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public static class SetupResult {
|
public static class SetupResult {
|
||||||
/** Result code if verity is set up correctly. */
|
/** Result code if verity is set up correctly. */
|
||||||
private static final int RESULT_OK = 1;
|
private static final int RESULT_OK = 1;
|
||||||
@@ -401,30 +291,4 @@ abstract public class VerityUtils {
|
|||||||
return mBuffer == null ? -1 : mBuffer.limit();
|
return mBuffer == null ? -1 : mBuffer.limit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A {@code ByteBufferFactory} that tracks the {@code ByteBuffer} it creates. */
|
|
||||||
private static class TrackedBufferFactory implements ByteBufferFactory {
|
|
||||||
private ByteBuffer mBuffer;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer create(int capacity) {
|
|
||||||
if (mBuffer != null) {
|
|
||||||
throw new IllegalStateException("Multiple instantiation from this factory");
|
|
||||||
}
|
|
||||||
mBuffer = ByteBuffer.allocate(capacity);
|
|
||||||
return mBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer getBuffer() {
|
|
||||||
return mBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Round up the number to the next multiple of the divisor. */
|
|
||||||
private static long roundUpToNextMultiple(long number, long divisor) {
|
|
||||||
if (number > (Long.MAX_VALUE - divisor)) {
|
|
||||||
throw new IllegalArgumentException("arithmetic overflow");
|
|
||||||
}
|
|
||||||
return ((number + (divisor - 1)) / divisor) * divisor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,61 +36,33 @@
|
|||||||
#include <linux/fsverity.h>
|
#include <linux/fsverity.h>
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Before fs-verity is upstreamed, use the current snapshot for development.
|
|
||||||
// https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/tree/include/uapi/linux/fsverity.h?h=fsverity
|
|
||||||
|
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <linux/ioctl.h>
|
#include <linux/ioctl.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define FS_VERITY_HASH_ALG_SHA256 1
|
||||||
|
|
||||||
|
struct fsverity_enable_arg {
|
||||||
|
__u32 version;
|
||||||
|
__u32 hash_algorithm;
|
||||||
|
__u32 block_size;
|
||||||
|
__u32 salt_size;
|
||||||
|
__u64 salt_ptr;
|
||||||
|
__u32 sig_size;
|
||||||
|
__u32 __reserved1;
|
||||||
|
__u64 sig_ptr;
|
||||||
|
__u64 __reserved2[11];
|
||||||
|
};
|
||||||
|
|
||||||
struct fsverity_digest {
|
struct fsverity_digest {
|
||||||
__u16 digest_algorithm;
|
__u16 digest_algorithm;
|
||||||
__u16 digest_size; /* input/output */
|
__u16 digest_size; /* input/output */
|
||||||
__u8 digest[];
|
__u8 digest[];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FS_IOC_ENABLE_VERITY _IO('f', 133)
|
#define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg)
|
||||||
#define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest)
|
#define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest)
|
||||||
|
|
||||||
#define FS_VERITY_MAGIC "FSVerity"
|
|
||||||
|
|
||||||
#define FS_VERITY_ALG_SHA256 1
|
|
||||||
|
|
||||||
struct fsverity_descriptor {
|
|
||||||
__u8 magic[8]; /* must be FS_VERITY_MAGIC */
|
|
||||||
__u8 major_version; /* must be 1 */
|
|
||||||
__u8 minor_version; /* must be 0 */
|
|
||||||
__u8 log_data_blocksize;/* log2(data-bytes-per-hash), e.g. 12 for 4KB */
|
|
||||||
__u8 log_tree_blocksize;/* log2(tree-bytes-per-hash), e.g. 12 for 4KB */
|
|
||||||
__le16 data_algorithm; /* hash algorithm for data blocks */
|
|
||||||
__le16 tree_algorithm; /* hash algorithm for tree blocks */
|
|
||||||
__le32 flags; /* flags */
|
|
||||||
__le32 __reserved1; /* must be 0 */
|
|
||||||
__le64 orig_file_size; /* size of the original file data */
|
|
||||||
__le16 auth_ext_count; /* number of authenticated extensions */
|
|
||||||
__u8 __reserved2[30]; /* must be 0 */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FS_VERITY_EXT_ROOT_HASH 1
|
|
||||||
#define FS_VERITY_EXT_PKCS7_SIGNATURE 3
|
|
||||||
|
|
||||||
struct fsverity_extension {
|
|
||||||
__le32 length;
|
|
||||||
__le16 type; /* Type of this extension (see codes above) */
|
|
||||||
__le16 __reserved; /* Reserved, must be 0 */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fsverity_digest_disk {
|
|
||||||
__le16 digest_algorithm;
|
|
||||||
__le16 digest_size;
|
|
||||||
__u8 digest[];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fsverity_footer {
|
|
||||||
__le32 desc_reverse_offset; /* distance to fsverity_descriptor */
|
|
||||||
__u8 magic[8]; /* FS_VERITY_MAGIC */
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const int kSha256Bytes = 32;
|
const int kSha256Bytes = 32;
|
||||||
@@ -99,52 +71,24 @@ namespace android {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class JavaByteArrayHolder {
|
int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
|
||||||
public:
|
|
||||||
JavaByteArrayHolder(const JavaByteArrayHolder &other) = delete;
|
|
||||||
JavaByteArrayHolder(JavaByteArrayHolder &&other)
|
|
||||||
: mEnv(other.mEnv), mBytes(other.mBytes), mElements(other.mElements) {
|
|
||||||
other.mElements = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JavaByteArrayHolder newArray(JNIEnv* env, jsize size) {
|
|
||||||
return JavaByteArrayHolder(env, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
jbyte* getRaw() {
|
|
||||||
return mElements;
|
|
||||||
}
|
|
||||||
|
|
||||||
jbyteArray release() {
|
|
||||||
mEnv->ReleaseByteArrayElements(mBytes, mElements, 0);
|
|
||||||
mElements = nullptr;
|
|
||||||
return mBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
~JavaByteArrayHolder() {
|
|
||||||
LOG_ALWAYS_FATAL_IF(mElements != nullptr, "Elements are not released");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
JavaByteArrayHolder(JNIEnv* env, jsize size) {
|
|
||||||
mEnv = env;
|
|
||||||
mBytes = mEnv->NewByteArray(size);
|
|
||||||
mElements = mEnv->GetByteArrayElements(mBytes, nullptr);
|
|
||||||
memset(mElements, 0, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEnv* mEnv;
|
|
||||||
jbyteArray mBytes;
|
|
||||||
jbyte* mElements;
|
|
||||||
};
|
|
||||||
|
|
||||||
int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
|
|
||||||
const char* path = env->GetStringUTFChars(filePath, nullptr);
|
const char* path = env->GetStringUTFChars(filePath, nullptr);
|
||||||
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
|
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
|
||||||
|
env->ReleaseStringUTFChars(filePath, path);
|
||||||
if (rfd.get() < 0) {
|
if (rfd.get() < 0) {
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) {
|
|
||||||
|
fsverity_enable_arg arg = {};
|
||||||
|
arg.version = 1;
|
||||||
|
arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
|
||||||
|
arg.block_size = 4096;
|
||||||
|
arg.salt_size = 0;
|
||||||
|
arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
|
||||||
|
arg.sig_size = env->GetArrayLength(signature);
|
||||||
|
arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
|
||||||
|
|
||||||
|
if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -159,6 +103,7 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
|
|||||||
|
|
||||||
const char* path = env->GetStringUTFChars(filePath, nullptr);
|
const char* path = env->GetStringUTFChars(filePath, nullptr);
|
||||||
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
|
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
|
||||||
|
env->ReleaseStringUTFChars(filePath, path);
|
||||||
if (rfd.get() < 0) {
|
if (rfd.get() < 0) {
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
@@ -168,71 +113,9 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) {
|
|
||||||
auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes);
|
|
||||||
fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii.getRaw());
|
|
||||||
|
|
||||||
data->digest_algorithm = FS_VERITY_ALG_SHA256;
|
|
||||||
data->digest_size = kSha256Bytes;
|
|
||||||
if (env->GetArrayLength(digest) != kSha256Bytes) {
|
|
||||||
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid hash size of %d",
|
|
||||||
env->GetArrayLength(digest));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const jbyte* src = env->GetByteArrayElements(digest, nullptr);
|
|
||||||
memcpy(data->digest, src, kSha256Bytes);
|
|
||||||
|
|
||||||
return raii.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) {
|
|
||||||
auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor));
|
|
||||||
fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii.getRaw());
|
|
||||||
|
|
||||||
memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic));
|
|
||||||
desc->major_version = 1;
|
|
||||||
desc->minor_version = 0;
|
|
||||||
desc->log_data_blocksize = 12;
|
|
||||||
desc->log_tree_blocksize = 12;
|
|
||||||
desc->data_algorithm = FS_VERITY_ALG_SHA256;
|
|
||||||
desc->tree_algorithm = FS_VERITY_ALG_SHA256;
|
|
||||||
desc->flags = 0;
|
|
||||||
desc->orig_file_size = fileSize;
|
|
||||||
desc->auth_ext_count = 1;
|
|
||||||
|
|
||||||
return raii.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId,
|
|
||||||
jint extensionDataSize) {
|
|
||||||
auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension));
|
|
||||||
fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii.getRaw());
|
|
||||||
|
|
||||||
ext->length = sizeof(fsverity_extension) + extensionDataSize;
|
|
||||||
ext->type = extensionId;
|
|
||||||
|
|
||||||
return raii.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */,
|
|
||||||
jint offsetToDescriptorHead) {
|
|
||||||
auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer));
|
|
||||||
fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii.getRaw());
|
|
||||||
|
|
||||||
footer->desc_reverse_offset = offsetToDescriptorHead + sizeof(fsverity_footer);
|
|
||||||
memcpy(footer->magic, FS_VERITY_MAGIC, sizeof(footer->magic));
|
|
||||||
|
|
||||||
return raii.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
const JNINativeMethod sMethods[] = {
|
const JNINativeMethod sMethods[] = {
|
||||||
{ "enableFsverityNative", "(Ljava/lang/String;)I", (void *)enableFsverity },
|
{ "enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity },
|
||||||
{ "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
|
{ "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
|
||||||
{ "constructFsveritySignedDataNative", "([B)[B", (void *)constructFsveritySignedData },
|
|
||||||
{ "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor },
|
|
||||||
{ "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension },
|
|
||||||
{ "constructFsverityFooterNative", "(I)[B", (void *)constructFsverityFooter },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user