1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.security; 18 19 import android.annotation.NonNull; 20 import android.os.Build; 21 import android.os.SystemProperties; 22 import android.system.Os; 23 import android.system.OsConstants; 24 import android.util.Slog; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.nio.file.Files; 29 import java.nio.file.Paths; 30 31 /** Provides fsverity related operations. */ 32 public abstract class VerityUtils { 33 private static final String TAG = "VerityUtils"; 34 35 /** 36 * File extension of the signature file. For example, foo.apk.fsv_sig is the signature file of 37 * foo.apk. 38 */ 39 public static final String FSVERITY_SIGNATURE_FILE_EXTENSION = ".fsv_sig"; 40 41 /** The maximum size of signature file. This is just to avoid potential abuse. */ 42 private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192; 43 44 /** SHA256 hash size. */ 45 private static final int HASH_SIZE_BYTES = 32; 46 isFsVeritySupported()47 public static boolean isFsVeritySupported() { 48 return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.R 49 || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2; 50 } 51 52 /** Returns true if the given file looks like containing an fs-verity signature. */ isFsveritySignatureFile(File file)53 public static boolean isFsveritySignatureFile(File file) { 54 return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION); 55 } 56 57 /** Returns the fs-verity signature file path of the given file. */ getFsveritySignatureFilePath(String filePath)58 public static String getFsveritySignatureFilePath(String filePath) { 59 return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION; 60 } 61 62 /** Enables fs-verity for the file with a PKCS#7 detached signature file. */ setUpFsverity(@onNull String filePath, @NonNull String signaturePath)63 public static void setUpFsverity(@NonNull String filePath, @NonNull String signaturePath) 64 throws IOException { 65 if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) { 66 throw new SecurityException("Signature file is unexpectedly large: " + signaturePath); 67 } 68 setUpFsverity(filePath, Files.readAllBytes(Paths.get(signaturePath))); 69 } 70 71 /** Enables fs-verity for the file with a PKCS#7 detached signature bytes. */ setUpFsverity(@onNull String filePath, @NonNull byte[] pkcs7Signature)72 public static void setUpFsverity(@NonNull String filePath, @NonNull byte[] pkcs7Signature) 73 throws IOException { 74 // This will fail if the public key is not already in .fs-verity kernel keyring. 75 int errno = enableFsverityNative(filePath, pkcs7Signature); 76 if (errno != 0) { 77 throw new IOException("Failed to enable fs-verity on " + filePath + ": " 78 + Os.strerror(errno)); 79 } 80 } 81 82 /** Returns whether the file has fs-verity enabled. */ hasFsverity(@onNull String filePath)83 public static boolean hasFsverity(@NonNull String filePath) { 84 int retval = statxForFsverityNative(filePath); 85 if (retval < 0) { 86 Slog.e(TAG, "Failed to check whether fs-verity is enabled, errno " + -retval + ": " 87 + filePath); 88 return false; 89 } 90 return (retval == 1); 91 } 92 93 /** Returns hash of a root node for the fs-verity enabled file. */ getFsverityRootHash(@onNull String filePath)94 public static byte[] getFsverityRootHash(@NonNull String filePath) { 95 byte[] result = new byte[HASH_SIZE_BYTES]; 96 int retval = measureFsverityNative(filePath, result); 97 if (retval < 0) { 98 if (retval != -OsConstants.ENODATA) { 99 Slog.e(TAG, "Failed to measure fs-verity, errno " + -retval + ": " + filePath); 100 } 101 return null; 102 } 103 return result; 104 } 105 enableFsverityNative(@onNull String filePath, @NonNull byte[] pkcs7Signature)106 private static native int enableFsverityNative(@NonNull String filePath, 107 @NonNull byte[] pkcs7Signature); measureFsverityNative(@onNull String filePath, @NonNull byte[] digest)108 private static native int measureFsverityNative(@NonNull String filePath, 109 @NonNull byte[] digest); statxForFsverityNative(@onNull String filePath)110 private static native int statxForFsverityNative(@NonNull String filePath); 111 } 112