1 /* 2 * Copyright (C) 2023 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.fsverity; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertThrows; 22 23 import android.content.Context; 24 import android.security.FileIntegrityManager; 25 import android.util.Log; 26 27 import androidx.test.core.app.ApplicationProvider; 28 import androidx.test.platform.app.InstrumentationRegistry; 29 30 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 31 32 import org.junit.Rule; 33 import org.junit.Test; 34 35 import java.io.FileOutputStream; 36 import java.io.IOException; 37 import java.io.RandomAccessFile; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 41 /** 42 * Test helper that works with the host-side test to set up a test file, and to verify fs-verity 43 * verification is done expectedly. 44 */ 45 public class Helper { 46 private static final String TAG = "FsVerityTest"; 47 48 private static final String FILENAME = "test.file"; 49 50 private static final long BLOCK_SIZE = 4096; 51 52 @Rule 53 public final AdoptShellPermissionsRule mAdoptShellPermissionsRule = 54 new AdoptShellPermissionsRule( 55 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 56 android.Manifest.permission.SETUP_FSVERITY); 57 58 @Test prepareTest()59 public void prepareTest() throws Exception { 60 Context context = ApplicationProvider.getApplicationContext(); 61 android.os.Bundle testArgs = InstrumentationRegistry.getArguments(); 62 63 String basename = testArgs.getString("basename"); 64 context.deleteFile(basename); 65 66 assertThat(testArgs).isNotNull(); 67 int fileSize = Integer.parseInt(testArgs.getString("fileSize")); 68 Log.d(TAG, "Preparing test file with size " + fileSize); 69 70 byte[] bytes = new byte[8192]; 71 Arrays.fill(bytes, (byte) '1'); 72 try (FileOutputStream os = context.openFileOutput(basename, Context.MODE_PRIVATE)) { 73 for (int i = 0; i < fileSize; i += bytes.length) { 74 if (i + bytes.length > fileSize) { 75 os.write(bytes, 0, fileSize % bytes.length); 76 } else { 77 os.write(bytes); 78 } 79 } 80 } 81 82 // Enable fs-verity 83 FileIntegrityManager fim = context.getSystemService(FileIntegrityManager.class); 84 fim.setupFsVerity(context.getFileStreamPath(basename)); 85 } 86 87 @Test verifyFileRead()88 public void verifyFileRead() throws Exception { 89 Context context = ApplicationProvider.getApplicationContext(); 90 91 // Collect indices that the backing blocks are supposed to be corrupted. 92 android.os.Bundle testArgs = InstrumentationRegistry.getArguments(); 93 assertThat(testArgs).isNotNull(); 94 String filePath = testArgs.getString("filePath"); 95 String csv = testArgs.getString("brokenBlockIndicesCsv"); 96 Log.d(TAG, "brokenBlockIndicesCsv: " + csv); 97 String[] strings = csv.split(","); 98 var corrupted = new ArrayList(strings.length); 99 for (int i = 0; i < strings.length; i++) { 100 corrupted.add(Integer.parseInt(strings[i])); 101 } 102 103 // Expect the read to succeed or fail per the prior. 104 try (var file = new RandomAccessFile(filePath, "r")) { 105 long total_blocks = (file.length() + BLOCK_SIZE - 1) / BLOCK_SIZE; 106 for (int i = 0; i < (int) total_blocks; i++) { 107 file.seek(i * BLOCK_SIZE); 108 if (corrupted.contains(i)) { 109 Log.d(TAG, "Expecting read at block #" + i + " to fail"); 110 assertThrows(IOException.class, () -> file.read()); 111 } else { 112 assertThat(file.readByte()).isEqualTo('1'); 113 } 114 } 115 } 116 } 117 } 118