blob: bf274b035b20686f79ee0f40376d7e7df4c68ce7 [file] [log] [blame]
Alan Stokes9fd57b02024-05-28 09:50:22 +01001/*
2 * Copyright 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//! Rust wrapper for the VM Payload API, allowing virtual machine payload code to be written in
18//! Rust. This wraps the raw C API, accessed via bindgen, into a more idiomatic Rust interface.
19//!
Jiyong Parkb51c0282024-07-22 12:58:26 +090020//! See `https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Virtualization/libs/libvm_payload/README.md`
Alan Stokes9fd57b02024-05-28 09:50:22 +010021//! for more information on the VM Payload API.
22
23mod attestation;
24
Alan Stokesc874cd22024-06-11 16:23:18 +010025pub use attestation::{request_attestation, AttestationError, AttestationResult};
Alan Stokes9fd57b02024-05-28 09:50:22 +010026use binder::unstable_api::AsNative;
27use binder::{FromIBinder, Strong};
28use std::ffi::{c_void, CStr, OsStr};
29use std::os::unix::ffi::OsStrExt;
30use std::path::Path;
31use std::ptr;
32use vm_payload_bindgen::{
33 AIBinder, AVmPayload_getApkContentsPath, AVmPayload_getEncryptedStoragePath,
Shikha Panwar5b7b4942024-12-18 15:32:49 +000034 AVmPayload_getVmInstanceSecret, AVmPayload_isNewInstance, AVmPayload_notifyPayloadReady,
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +000035 AVmPayload_readRollbackProtectedSecret, AVmPayload_runVsockRpcServer,
36 AVmPayload_writeRollbackProtectedSecret,
Alan Stokes9fd57b02024-05-28 09:50:22 +010037};
38
Alan Stokesc874cd22024-06-11 16:23:18 +010039/// The functions declared here are restricted to VMs created with a config file;
40/// they will fail, or panic, if called in other VMs. The ability to create such VMs
41/// requires the android.permission.USE_CUSTOM_VIRTUAL_MACHINE permission, and is
42/// therefore not available to privileged or third party apps.
43///
44/// These functions can be used by tests, if the permission is granted via shell.
45pub mod restricted {
46 pub use crate::attestation::request_attestation_for_testing;
47}
48
Alan Stokes9fd57b02024-05-28 09:50:22 +010049/// Marks the main function of the VM payload.
50///
51/// When the VM is run, this function is called. If it returns, the VM ends normally with a 0 exit
52/// code.
53///
54/// Example:
55///
56/// ```rust
57/// use log::info;
58///
59/// vm_payload::main!(vm_main);
60///
61/// fn vm_main() {
62/// android_logger::init_once(
63/// android_logger::Config::default()
64/// .with_tag("example_vm_payload")
65/// .with_max_level(log::LevelFilter::Info),
66/// );
67/// info!("Hello world");
68/// }
69/// ```
70#[macro_export]
71macro_rules! main {
72 ($name:path) => {
73 // Export a symbol with a name matching the extern declaration below.
74 #[export_name = "rust_main"]
75 fn __main() {
76 // Ensure that the main function provided by the application has the correct type.
77 $name()
78 }
79 };
80}
81
82// This is the real C entry point for the VM; we just forward to the Rust entry point.
83#[allow(non_snake_case)]
84#[no_mangle]
85extern "C" fn AVmPayload_main() {
86 extern "Rust" {
87 fn rust_main();
88 }
89
90 // SAFETY: rust_main is provided by the application using the `main!` macro above, which makes
91 // sure it has the right type.
92 unsafe { rust_main() }
93}
94
95/// Notifies the host that the payload is ready.
96///
97/// If the host app has set a `VirtualMachineCallback` for the VM, its
98/// `onPayloadReady` method will be called.
99///
100/// Note that subsequent calls to this function after the first have no effect;
101/// `onPayloadReady` is never called more than once.
102pub fn notify_payload_ready() {
103 // SAFETY: Invokes a method from the bindgen library `vm_payload_bindgen` which is safe to
104 // call at any time.
105 unsafe { AVmPayload_notifyPayloadReady() };
106}
107
108/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
109/// port.
110///
111/// If and when the server is ready for connections (i.e. it is listening on the port),
112/// [`notify_payload_ready`] is called to notify the host that the server is ready. This is
113/// appropriate for VM payloads that serve a single binder service - which is common.
114///
115/// Note that this function does not return. The calling thread joins the binder
116/// thread pool to handle incoming messages.
117pub fn run_single_vsock_service<T>(service: Strong<T>, port: u32) -> !
118where
119 T: FromIBinder + ?Sized,
120{
121 extern "C" fn on_ready(_param: *mut c_void) {
122 notify_payload_ready();
123 }
124
125 let mut service = service.as_binder();
126 // The cast here is needed because the compiler doesn't know that our vm_payload_bindgen
127 // AIBinder is the same type as binder_ndk_sys::AIBinder.
128 let service = service.as_native_mut() as *mut AIBinder;
129 let param = ptr::null_mut();
130 // SAFETY: We have a strong reference to the service, so the raw pointer remains valid. It is
131 // safe for on_ready to be invoked at any time, with any parameter.
132 unsafe { AVmPayload_runVsockRpcServer(service, port, Some(on_ready), param) }
133}
134
135/// Gets the path to the contents of the APK containing the VM payload. It is a directory, under
136/// which are the unzipped contents of the APK containing the payload, all read-only
137/// but accessible to the payload.
138pub fn apk_contents_path() -> &'static Path {
139 // SAFETY: AVmPayload_getApkContentsPath always returns a non-null pointer to a
140 // nul-terminated C string with static lifetime.
141 let c_str = unsafe { CStr::from_ptr(AVmPayload_getApkContentsPath()) };
142 Path::new(OsStr::from_bytes(c_str.to_bytes()))
143}
144
145/// Gets the path to the encrypted persistent storage for the VM, if any. This is
146/// a directory under which any files or directories created will be stored on
147/// behalf of the VM by the host app. All data is encrypted using a key known
148/// only to the VM, so the host cannot decrypt it, but may delete it.
149///
150/// Returns `None` if no encrypted storage was requested in the VM configuration.
151pub fn encrypted_storage_path() -> Option<&'static Path> {
152 // SAFETY: AVmPayload_getEncryptedStoragePath returns either null or a pointer to a
153 // nul-terminated C string with static lifetime.
154 let ptr = unsafe { AVmPayload_getEncryptedStoragePath() };
155 if ptr.is_null() {
156 None
157 } else {
158 // SAFETY: We know the pointer is not null, and so it is a valid C string.
159 let c_str = unsafe { CStr::from_ptr(ptr) };
160 Some(Path::new(OsStr::from_bytes(c_str.to_bytes())))
161 }
162}
163
164/// Retrieves all or part of a 32-byte secret that is bound to this unique VM
165/// instance and the supplied identifier. The secret can be used e.g. as an
166/// encryption key.
167///
168/// Every VM has a secret that is derived from a device-specific value known to
169/// the hypervisor, the code that runs in the VM and its non-modifiable
170/// configuration; it is not made available to the host OS.
171///
172/// This function performs a further derivation from the VM secret and the
173/// supplied identifier. As long as the VM identity doesn't change the same value
174/// will be returned for the same identifier, even if the VM is stopped &
175/// restarted or the device rebooted.
176///
177/// If multiple secrets are required for different purposes, a different
178/// identifier should be used for each. The identifiers otherwise are arbitrary
179/// byte sequences and do not need to be kept secret; typically they are
180/// hardcoded in the calling code.
181///
182/// The secret is returned in [`secret`], truncated to its size, which must be between
183/// 1 and 32 bytes (inclusive) or the function will panic.
Alan Stokesc874cd22024-06-11 16:23:18 +0100184pub fn get_vm_instance_secret(identifier: &[u8], secret: &mut [u8]) {
Alan Stokes9fd57b02024-05-28 09:50:22 +0100185 let secret_size = secret.len();
186 assert!((1..=32).contains(&secret_size), "VM instance secrets can be up to 32 bytes long");
187
188 // SAFETY: The function only reads from `[identifier]` within its bounds, and only writes to
189 // `[secret]` within its bounds. Neither reference is retained, and we know neither is null.
190 unsafe {
191 AVmPayload_getVmInstanceSecret(
192 identifier.as_ptr() as *const c_void,
193 identifier.len(),
194 secret.as_mut_ptr() as *mut c_void,
195 secret_size,
196 )
197 }
198}
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +0000199
200/// Read payload's `data` written on behalf of the payload in Secretkeeper.
201pub fn read_rollback_protected_secret(data: &mut [u8]) -> i32 {
202 // SAFETY: The function only reads from`[data]` within its bounds.
203 unsafe { AVmPayload_readRollbackProtectedSecret(data.as_ptr() as *mut c_void, data.len()) }
204}
205
206/// Write `data`, on behalf of the payload, to Secretkeeper.
207pub fn write_rollback_protected_secret(data: &[u8]) -> i32 {
208 // SAFETY: The function only writes to `[data]` within its bounds.
209 unsafe { AVmPayload_writeRollbackProtectedSecret(data.as_ptr() as *const c_void, data.len()) }
210}
Shikha Panwar5b7b4942024-12-18 15:32:49 +0000211
212/// Checks whether the VM instance is new - i.e., if this is the first run of an instance.
213/// This is an indication of fresh new VM secrets. Payload can use this to setup the fresh
214/// instance if needed.
215pub fn is_new_instance_status() -> bool {
216 // SAFETY: The function returns bool, no arguments are needed.
217 unsafe { AVmPayload_isNewInstance() }
218}