1 /* 2 * Copyright (C) 2024 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.virt.rkpd.vm_attestation.testapp; 18 19 import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_FULL; 20 21 import static com.google.common.truth.TruthJUnit.assume; 22 23 import android.net.ConnectivityManager; 24 import android.net.NetworkCapabilities; 25 import android.net.Network; 26 import android.content.Context; 27 import android.system.virtualmachine.VirtualMachine; 28 import android.system.virtualmachine.VirtualMachineConfig; 29 30 import com.android.microdroid.test.device.MicrodroidDeviceTestBase; 31 import com.android.virt.vm_attestation.testservice.IAttestationService.SigningResult; 32 import com.android.virt.vm_attestation.util.X509Utils; 33 import android.system.virtualmachine.VirtualMachineManager; 34 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 import org.junit.runners.Parameterized; 39 40 import java.security.cert.X509Certificate; 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.Collection; 44 import java.util.List; 45 46 /** 47 * End-to-end test for the pVM remote attestation. 48 * 49 * <p>The test checks the two major steps of the pVM remote attestation: 50 * 51 * <p>1. Key provisioning: The test provisions AVF keys from the RKP server and verifies that the 52 * keys are for AVF. 53 * 54 * <p>2. VM attestation: The test creates a VM with a payload binary that requests to attest the VM, 55 * and then signs a message with the attestation key. 56 * 57 * <p>To run this test, you need to: 58 * 59 * <p>- Have an arm64 device supporting protected VMs. 60 * 61 * <p>- Have a stable network connection on the device. 62 */ 63 @RunWith(Parameterized.class) 64 public class RkpdVmAttestationTest extends MicrodroidDeviceTestBase { 65 private static final String TAG = "RkpdVmAttestationTest"; 66 67 private static final String VM_PAYLOAD_PATH = "libvm_attestation_test_payload.so"; 68 private static final String MESSAGE = "Hello RKP from AVF!"; 69 private static final String TEST_APP_PACKAGE_NAME = 70 "com.android.virt.rkpd.vm_attestation.testapp"; 71 72 @Parameterized.Parameter(0) 73 public String mGki; 74 75 @Parameterized.Parameters(name = "gki={0}") params()76 public static Collection<Object[]> params() { 77 List<Object[]> ret = new ArrayList<>(); 78 ret.add(new Object[] {null /* use microdroid kernel */}); 79 for (String gki : SUPPORTED_GKI_VERSIONS) { 80 ret.add(new Object[] {gki}); 81 } 82 return ret; 83 } 84 85 @Before setUp()86 public void setUp() throws Exception { 87 assume().withMessage("RKP Integration tests rely on network availability.") 88 .that(isNetworkConnected(getContext())) 89 .isTrue(); 90 assumeFeatureEnabled(VirtualMachineManager.FEATURE_REMOTE_ATTESTATION); 91 assume().withMessage("Test needs Remote Attestation support") 92 .that(getVirtualMachineManager().isRemoteAttestationSupported()) 93 .isTrue(); 94 95 if (mGki == null) { 96 // We don't need this permission to use the microdroid kernel. 97 revokePermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION); 98 } else { 99 // The permission is needed to use the GKI kernel. 100 // Granting the permission is needed as the microdroid kernel test setup 101 // can revoke the permission before the GKI kernel test. 102 grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION); 103 } 104 prepareTestSetup(true /* protectedVm */, mGki); 105 setMaxPerformanceTaskProfile(); 106 } 107 108 @Test usingProvisionedKeyForVmAttestationSucceeds()109 public void usingProvisionedKeyForVmAttestationSucceeds() throws Exception { 110 // Arrange. 111 VirtualMachineConfig config = 112 newVmConfigBuilderWithPayloadBinary(VM_PAYLOAD_PATH) 113 .setDebugLevel(DEBUG_LEVEL_FULL) 114 .setVmOutputCaptured(true) 115 .build(); 116 VirtualMachine vm = forceCreateNewVirtualMachine("attestation_with_rkpd_client", config); 117 byte[] challenge = new byte[32]; 118 Arrays.fill(challenge, (byte) 0xab); 119 120 // Act. 121 SigningResult signingResult = 122 runVmAttestationService(TAG, vm, challenge, MESSAGE.getBytes()); 123 124 // Assert. 125 X509Certificate[] certs = 126 X509Utils.validateAndParseX509CertChain(signingResult.certificateChain); 127 X509Utils.verifyAvfRelatedCerts(certs, challenge, TEST_APP_PACKAGE_NAME); 128 X509Utils.verifySignature(certs[0], MESSAGE.getBytes(), signingResult.signature); 129 } 130 isNetworkConnected(Context context)131 private static boolean isNetworkConnected(Context context) { 132 ConnectivityManager cm = context.getSystemService(ConnectivityManager.class); 133 Network network = cm.getActiveNetwork(); 134 NetworkCapabilities capabilities = cm.getNetworkCapabilities(network); 135 return capabilities != null 136 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 137 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); 138 } 139 } 140