blob: eb817525cffcfa01123ddb95992ed8d8f988dc52 [file] [log] [blame]
Alice Wangfb46ee12022-09-30 13:08:52 +00001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Alice Wangc474a0f2024-02-06 13:11:57 +000015//! This module handles the interaction with virtual machine payload service.
Alice Wangfb46ee12022-09-30 13:08:52 +000016
Alice Wangc474a0f2024-02-06 13:11:57 +000017use android_system_virtualization_payload::aidl::android::system::virtualization::payload:: IVmPayloadService::{
18 IVmPayloadService, ENCRYPTEDSTORE_MOUNTPOINT, VM_APK_CONTENTS_PATH,
19 VM_PAYLOAD_SERVICE_SOCKET_NAME, AttestationResult::AttestationResult,
Andrew Sculld64ae7d2022-10-05 17:41:43 +000020};
Alice Wangc474a0f2024-02-06 13:11:57 +000021use anyhow::{bail, ensure, Context, Result};
22use binder::{
23 unstable_api::{new_spibinder, AIBinder},
24 Strong, ExceptionCode,
25};
Alice Wangc474a0f2024-02-06 13:11:57 +000026use log::{error, info, LevelFilter};
27use rpcbinder::{RpcServer, RpcSession};
28use openssl::{ec::EcKey, sha::sha256, ecdsa::EcdsaSig};
29use std::convert::Infallible;
30use std::ffi::{CString, CStr};
31use std::fmt::Debug;
32use std::os::raw::{c_char, c_void};
33use std::path::Path;
34use std::ptr::{self, NonNull};
35use std::sync::{
36 atomic::{AtomicBool, Ordering},
Andrew Walbran9c03a3a2024-09-03 12:12:59 +010037 LazyLock,
Alice Wangc474a0f2024-02-06 13:11:57 +000038 Mutex,
39};
Alice Wang1715f372024-02-14 10:51:52 +000040use vm_payload_status_bindgen::AVmAttestationStatus;
Alice Wangc474a0f2024-02-06 13:11:57 +000041
Alice Wange64dd182024-01-17 15:57:55 +000042/// Maximum size of an ECDSA signature for EC P-256 key is 72 bytes.
43const MAX_ECDSA_P256_SIGNATURE_SIZE: usize = 72;
44
Andrew Walbran9c03a3a2024-09-03 12:12:59 +010045static VM_APK_CONTENTS_PATH_C: LazyLock<CString> =
46 LazyLock::new(|| CString::new(VM_APK_CONTENTS_PATH).expect("CString::new failed"));
47static PAYLOAD_CONNECTION: Mutex<Option<Strong<dyn IVmPayloadService>>> = Mutex::new(None);
48static VM_ENCRYPTED_STORAGE_PATH_C: LazyLock<CString> =
49 LazyLock::new(|| CString::new(ENCRYPTEDSTORE_MOUNTPOINT).expect("CString::new failed"));
Alice Wangc474a0f2024-02-06 13:11:57 +000050
51static ALREADY_NOTIFIED: AtomicBool = AtomicBool::new(false);
52
53/// Return a connection to the payload service in Microdroid Manager. Uses the existing connection
54/// if there is one, otherwise attempts to create a new one.
55fn get_vm_payload_service() -> Result<Strong<dyn IVmPayloadService>> {
56 let mut connection = PAYLOAD_CONNECTION.lock().unwrap();
57 if let Some(strong) = &*connection {
58 Ok(strong.clone())
59 } else {
60 let new_connection: Strong<dyn IVmPayloadService> = RpcSession::new()
61 .setup_unix_domain_client(VM_PAYLOAD_SERVICE_SOCKET_NAME)
62 .context(format!("Failed to connect to service: {}", VM_PAYLOAD_SERVICE_SOCKET_NAME))?;
63 *connection = Some(new_connection.clone());
64 Ok(new_connection)
65 }
66}
67
68/// Make sure our logging goes to logcat. It is harmless to call this more than once.
69fn initialize_logging() {
70 android_logger::init_once(
71 android_logger::Config::default().with_tag("vm_payload").with_max_level(LevelFilter::Info),
72 );
73}
74
75/// In many cases clients can't do anything useful if API calls fail, and the failure
76/// generally indicates that the VM is exiting or otherwise doomed. So rather than
77/// returning a non-actionable error indication we just log the problem and abort
78/// the process.
79fn unwrap_or_abort<T, E: Debug>(result: Result<T, E>) -> T {
80 result.unwrap_or_else(|e| {
81 let msg = format!("{:?}", e);
82 error!("{msg}");
83 panic!("{msg}")
84 })
85}
86
87/// Notifies the host that the payload is ready.
88/// Panics on failure.
89#[no_mangle]
90pub extern "C" fn AVmPayload_notifyPayloadReady() {
91 initialize_logging();
92
93 if !ALREADY_NOTIFIED.swap(true, Ordering::Relaxed) {
94 unwrap_or_abort(try_notify_payload_ready());
95
96 info!("Notified host payload ready successfully");
97 }
98}
99
100/// Notifies the host that the payload is ready.
101/// Returns a `Result` containing error information if failed.
102fn try_notify_payload_ready() -> Result<()> {
103 get_vm_payload_service()?.notifyPayloadReady().context("Cannot notify payload ready")
104}
105
106/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
107/// port.
108///
109/// If and when the server is ready for connections (it is listening on the port), `on_ready` is
110/// called to allow appropriate action to be taken - e.g. to notify clients that they may now
111/// attempt to connect.
112///
113/// The current thread joins the binder thread pool to handle incoming messages.
114/// This function never returns.
115///
116/// Panics on error (including unexpected server exit).
117///
118/// # Safety
119///
120/// If present, the `on_ready` callback must be a valid function pointer, which will be called at
121/// most once, while this function is executing, with the `param` parameter.
122#[no_mangle]
123pub unsafe extern "C" fn AVmPayload_runVsockRpcServer(
124 service: *mut AIBinder,
125 port: u32,
126 on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>,
127 param: *mut c_void,
128) -> Infallible {
129 initialize_logging();
130
131 // SAFETY: try_run_vsock_server has the same requirements as this function
132 unwrap_or_abort(unsafe { try_run_vsock_server(service, port, on_ready, param) })
133}
134
135/// # Safety: Same as `AVmPayload_runVsockRpcServer`.
136unsafe fn try_run_vsock_server(
137 service: *mut AIBinder,
138 port: u32,
139 on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>,
140 param: *mut c_void,
141) -> Result<Infallible> {
142 // SAFETY: AIBinder returned has correct reference count, and the ownership can
143 // safely be taken by new_spibinder.
144 let service = unsafe { new_spibinder(service) };
145 if let Some(service) = service {
146 match RpcServer::new_vsock(service, libc::VMADDR_CID_HOST, port) {
Devin Moore068e6742024-10-28 18:16:25 +0000147 Ok((server, _)) => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000148 if let Some(on_ready) = on_ready {
149 // SAFETY: We're calling the callback with the parameter specified within the
150 // allowed lifetime.
151 unsafe { on_ready(param) };
152 }
153 server.join();
154 bail!("RpcServer unexpectedly terminated");
155 }
156 Err(err) => {
157 bail!("Failed to start RpcServer: {:?}", err);
158 }
159 }
160 } else {
161 bail!("Failed to convert the given service from AIBinder to SpIBinder.");
162 }
163}
164
165/// Get a secret that is uniquely bound to this VM instance.
166/// Panics on failure.
167///
168/// # Safety
169///
170/// Behavior is undefined if any of the following conditions are violated:
171///
172/// * `identifier` must be [valid] for reads of `identifier_size` bytes.
173/// * `secret` must be [valid] for writes of `size` bytes.
174///
175/// [valid]: ptr#safety
176#[no_mangle]
177pub unsafe extern "C" fn AVmPayload_getVmInstanceSecret(
178 identifier: *const u8,
179 identifier_size: usize,
180 secret: *mut u8,
181 size: usize,
182) {
183 initialize_logging();
184
185 // SAFETY: See the requirements on `identifier` above.
186 let identifier = unsafe { std::slice::from_raw_parts(identifier, identifier_size) };
187 let vm_secret = unwrap_or_abort(try_get_vm_instance_secret(identifier, size));
188
189 // SAFETY: See the requirements on `secret` above; `vm_secret` is known to have length `size`,
190 // and cannot overlap `secret` because we just allocated it.
191 unsafe {
192 ptr::copy_nonoverlapping(vm_secret.as_ptr(), secret, size);
193 }
194}
195
196fn try_get_vm_instance_secret(identifier: &[u8], size: usize) -> Result<Vec<u8>> {
197 let vm_secret = get_vm_payload_service()?
198 .getVmInstanceSecret(identifier, i32::try_from(size)?)
199 .context("Cannot get VM instance secret")?;
200 ensure!(
201 vm_secret.len() == size,
202 "Returned secret has {} bytes, expected {}",
203 vm_secret.len(),
204 size
205 );
206 Ok(vm_secret)
207}
208
209/// Get the VM's attestation chain.
210/// Panics on failure.
211///
212/// # Safety
213///
214/// Behavior is undefined if any of the following conditions are violated:
215///
216/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
217///
218/// [valid]: ptr#safety
219#[no_mangle]
220pub unsafe extern "C" fn AVmPayload_getDiceAttestationChain(data: *mut u8, size: usize) -> usize {
221 initialize_logging();
222
223 let chain = unwrap_or_abort(try_get_dice_attestation_chain());
224 if size != 0 {
225 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
226 // the length of either buffer, and `chain` cannot overlap `data` because we just allocated
227 // it. We allow data to be null, which is never valid, but only if size == 0 which is
228 // checked above.
229 unsafe { ptr::copy_nonoverlapping(chain.as_ptr(), data, std::cmp::min(chain.len(), size)) };
230 }
231 chain.len()
232}
233
234fn try_get_dice_attestation_chain() -> Result<Vec<u8>> {
235 get_vm_payload_service()?.getDiceAttestationChain().context("Cannot get attestation chain")
236}
237
238/// Get the VM's attestation CDI.
239/// Panics on failure.
240///
241/// # Safety
242///
243/// Behavior is undefined if any of the following conditions are violated:
244///
245/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
246///
247/// [valid]: ptr#safety
248#[no_mangle]
249pub unsafe extern "C" fn AVmPayload_getDiceAttestationCdi(data: *mut u8, size: usize) -> usize {
250 initialize_logging();
251
252 let cdi = unwrap_or_abort(try_get_dice_attestation_cdi());
253 if size != 0 {
254 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
255 // the length of either buffer, and `cdi` cannot overlap `data` because we just allocated
256 // it. We allow data to be null, which is never valid, but only if size == 0 which is
257 // checked above.
258 unsafe { ptr::copy_nonoverlapping(cdi.as_ptr(), data, std::cmp::min(cdi.len(), size)) };
259 }
260 cdi.len()
261}
262
263fn try_get_dice_attestation_cdi() -> Result<Vec<u8>> {
264 get_vm_payload_service()?.getDiceAttestationCdi().context("Cannot get attestation CDI")
265}
266
267/// Requests the remote attestation of the client VM.
268///
269/// The challenge will be included in the certificate chain in the attestation result,
270/// serving as proof of the freshness of the result.
271///
272/// # Safety
273///
274/// Behavior is undefined if any of the following conditions are violated:
275///
276/// * `challenge` must be [valid] for reads of `challenge_size` bytes.
Alice Wangc474a0f2024-02-06 13:11:57 +0000277///
278/// [valid]: ptr#safety
279#[no_mangle]
280pub unsafe extern "C" fn AVmPayload_requestAttestation(
281 challenge: *const u8,
282 challenge_size: usize,
283 res: &mut *mut AttestationResult,
Alice Wang1715f372024-02-14 10:51:52 +0000284) -> AVmAttestationStatus {
Alice Wange64dd182024-01-17 15:57:55 +0000285 // SAFETY: The caller guarantees that `challenge` is valid for reads and `res` is valid
286 // for writes.
287 unsafe {
288 request_attestation(
289 challenge,
290 challenge_size,
291 false, // test_mode
292 res,
293 )
294 }
295}
296
297/// Requests the remote attestation of the client VM for testing.
298///
299/// # Safety
300///
301/// Behavior is undefined if any of the following conditions are violated:
302///
303/// * `challenge` must be [valid] for reads of `challenge_size` bytes.
304///
305/// [valid]: ptr#safety
306#[no_mangle]
307pub unsafe extern "C" fn AVmPayload_requestAttestationForTesting(
308 challenge: *const u8,
309 challenge_size: usize,
310 res: &mut *mut AttestationResult,
Alice Wang1715f372024-02-14 10:51:52 +0000311) -> AVmAttestationStatus {
Alice Wange64dd182024-01-17 15:57:55 +0000312 // SAFETY: The caller guarantees that `challenge` is valid for reads and `res` is valid
313 // for writes.
314 unsafe {
315 request_attestation(
316 challenge,
317 challenge_size,
318 true, // test_mode
319 res,
320 )
321 }
322}
323
324/// Requests the remote attestation of the client VM.
325///
326/// # Safety
327///
328/// Behavior is undefined if any of the following conditions are violated:
329///
330/// * `challenge` must be [valid] for reads of `challenge_size` bytes.
331///
332/// [valid]: ptr#safety
333unsafe fn request_attestation(
334 challenge: *const u8,
335 challenge_size: usize,
336 test_mode: bool,
337 res: &mut *mut AttestationResult,
Alice Wang1715f372024-02-14 10:51:52 +0000338) -> AVmAttestationStatus {
Alice Wangc474a0f2024-02-06 13:11:57 +0000339 initialize_logging();
340 const MAX_CHALLENGE_SIZE: usize = 64;
341 if challenge_size > MAX_CHALLENGE_SIZE {
Alice Wang1715f372024-02-14 10:51:52 +0000342 return AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE;
Alice Wangc474a0f2024-02-06 13:11:57 +0000343 }
344 let challenge = if challenge_size == 0 {
345 &[]
346 } else {
347 // SAFETY: The caller guarantees that `challenge` is valid for reads of
348 // `challenge_size` bytes and `challenge_size` is not zero.
349 unsafe { std::slice::from_raw_parts(challenge, challenge_size) }
350 };
351 let service = unwrap_or_abort(get_vm_payload_service());
Alice Wange64dd182024-01-17 15:57:55 +0000352 match service.requestAttestation(challenge, test_mode) {
Alice Wangc474a0f2024-02-06 13:11:57 +0000353 Ok(attestation_res) => {
354 *res = Box::into_raw(Box::new(attestation_res));
Alice Wang1715f372024-02-14 10:51:52 +0000355 AVmAttestationStatus::ATTESTATION_OK
Alice Wangc474a0f2024-02-06 13:11:57 +0000356 }
357 Err(e) => {
358 error!("Remote attestation failed: {e:?}");
359 binder_status_to_attestation_status(e)
360 }
361 }
362}
363
Alice Wang1715f372024-02-14 10:51:52 +0000364fn binder_status_to_attestation_status(status: binder::Status) -> AVmAttestationStatus {
Alice Wangc474a0f2024-02-06 13:11:57 +0000365 match status.exception_code() {
Alice Wang1715f372024-02-14 10:51:52 +0000366 ExceptionCode::UNSUPPORTED_OPERATION => AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED,
367 _ => AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED,
Alice Wangc474a0f2024-02-06 13:11:57 +0000368 }
369}
370
371/// Converts the return value from `AVmPayload_requestAttestation` to a text string
372/// representing the error code.
373#[no_mangle]
Alice Wang1715f372024-02-14 10:51:52 +0000374pub extern "C" fn AVmAttestationStatus_toString(status: AVmAttestationStatus) -> *const c_char {
Alice Wangc474a0f2024-02-06 13:11:57 +0000375 let message = match status {
Alice Wang1715f372024-02-14 10:51:52 +0000376 AVmAttestationStatus::ATTESTATION_OK => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000377 CStr::from_bytes_with_nul(b"The remote attestation completes successfully.\0").unwrap()
378 }
Alice Wang1715f372024-02-14 10:51:52 +0000379 AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000380 CStr::from_bytes_with_nul(b"The challenge size is not between 0 and 64.\0").unwrap()
381 }
Alice Wang1715f372024-02-14 10:51:52 +0000382 AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000383 CStr::from_bytes_with_nul(b"Failed to attest the VM. Please retry at a later time.\0")
384 .unwrap()
385 }
Alice Wang1715f372024-02-14 10:51:52 +0000386 AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => CStr::from_bytes_with_nul(
Alice Wangc474a0f2024-02-06 13:11:57 +0000387 b"Remote attestation is not supported in the current environment.\0",
388 )
389 .unwrap(),
390 };
391 message.as_ptr()
392}
393
394/// Reads the DER-encoded ECPrivateKey structure specified in [RFC 5915 s3] for the
395/// EC P-256 private key from the provided attestation result.
396///
397/// # Safety
398///
399/// Behavior is undefined if any of the following conditions are violated:
400///
401/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
Chris Wailes63b67d72024-08-19 16:23:21 -0700402/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
403/// memory `res` points to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000404///
405/// [valid]: ptr#safety
406/// [RFC 5915 s3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
407#[no_mangle]
408pub unsafe extern "C" fn AVmAttestationResult_getPrivateKey(
409 res: &AttestationResult,
410 data: *mut u8,
411 size: usize,
412) -> usize {
413 let private_key = &res.privateKey;
414 if size != 0 {
415 let data = NonNull::new(data).expect("data must not be null when size > 0");
416 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
417 // the length of either buffer, and the caller ensures that `private_key` cannot overlap
418 // `data`. We allow data to be null, which is never valid, but only if size == 0
419 // which is checked above.
420 unsafe {
421 ptr::copy_nonoverlapping(
422 private_key.as_ptr(),
423 data.as_ptr(),
424 std::cmp::min(private_key.len(), size),
425 )
426 };
427 }
428 private_key.len()
429}
430
431/// Signs the given message using ECDSA P-256, the message is first hashed with SHA-256 and
432/// then it is signed with the attested EC P-256 private key in the attestation result.
433///
434/// # Safety
435///
436/// Behavior is undefined if any of the following conditions are violated:
437///
438/// * `message` must be [valid] for reads of `message_size` bytes.
439/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
Chris Wailes63b67d72024-08-19 16:23:21 -0700440/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
441/// memory `res` or `message` point to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000442///
443///
444/// [valid]: ptr#safety
445#[no_mangle]
446pub unsafe extern "C" fn AVmAttestationResult_sign(
447 res: &AttestationResult,
448 message: *const u8,
449 message_size: usize,
450 data: *mut u8,
451 size: usize,
452) -> usize {
Alice Wange64dd182024-01-17 15:57:55 +0000453 // A DER-encoded ECDSA signature can have varying sizes even with the same EC Key and message,
454 // due to the encoding of the random values r and s that are part of the signature.
455 if size == 0 {
456 return MAX_ECDSA_P256_SIGNATURE_SIZE;
457 }
Alice Wangc474a0f2024-02-06 13:11:57 +0000458 if message_size == 0 {
459 panic!("Message to be signed must not be empty.")
460 }
461 // SAFETY: See the requirements on `message` above.
462 let message = unsafe { std::slice::from_raw_parts(message, message_size) };
463 let signature = unwrap_or_abort(try_ecdsa_sign(message, &res.privateKey));
Alice Wange64dd182024-01-17 15:57:55 +0000464 let data = NonNull::new(data).expect("data must not be null when size > 0");
465 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
466 // the length of either buffer, and the caller ensures that `signature` cannot overlap
467 // `data`. We allow data to be null, which is never valid, but only if size == 0
468 // which is checked above.
469 unsafe {
470 ptr::copy_nonoverlapping(
471 signature.as_ptr(),
472 data.as_ptr(),
473 usize::min(signature.len(), size),
474 )
475 };
476 if size < signature.len() {
477 // If the buffer is too small, return the maximum size of the signature to allow the caller
478 // to allocate a buffer large enough to call this function again.
479 MAX_ECDSA_P256_SIGNATURE_SIZE
480 } else {
481 signature.len()
Alice Wangc474a0f2024-02-06 13:11:57 +0000482 }
Alice Wangc474a0f2024-02-06 13:11:57 +0000483}
484
485fn try_ecdsa_sign(message: &[u8], der_encoded_ec_private_key: &[u8]) -> Result<Vec<u8>> {
486 let private_key = EcKey::private_key_from_der(der_encoded_ec_private_key)?;
487 let digest = sha256(message);
488 let sig = EcdsaSig::sign(&digest, &private_key)?;
489 Ok(sig.to_der()?)
490}
491
492/// Gets the number of certificates in the certificate chain.
493#[no_mangle]
494pub extern "C" fn AVmAttestationResult_getCertificateCount(res: &AttestationResult) -> usize {
495 res.certificateChain.len()
496}
497
498/// Retrieves the certificate at the given `index` from the certificate chain in the provided
499/// attestation result.
500///
501/// # Safety
502///
503/// Behavior is undefined if any of the following conditions are violated:
504///
505/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
506/// * `index` must be within the range of [0, number of certificates). The number of certificates
507/// can be obtained with `AVmAttestationResult_getCertificateCount`.
Chris Wailes63b67d72024-08-19 16:23:21 -0700508/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
509/// memory `res` points to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000510///
511/// [valid]: ptr#safety
512#[no_mangle]
513pub unsafe extern "C" fn AVmAttestationResult_getCertificateAt(
514 res: &AttestationResult,
515 index: usize,
516 data: *mut u8,
517 size: usize,
518) -> usize {
519 let certificate =
520 &res.certificateChain.get(index).expect("The index is out of bounds.").encodedCertificate;
521 if size != 0 {
522 let data = NonNull::new(data).expect("data must not be null when size > 0");
523 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
524 // the length of either buffer, and the caller ensures that `certificate` cannot overlap
525 // `data`. We allow data to be null, which is never valid, but only if size == 0
526 // which is checked above.
527 unsafe {
528 ptr::copy_nonoverlapping(
529 certificate.as_ptr(),
530 data.as_ptr(),
531 std::cmp::min(certificate.len(), size),
532 )
533 };
534 }
535 certificate.len()
536}
537
538/// Frees all the data owned by given attestation result and result itself.
539///
540/// # Safety
541///
542/// Behavior is undefined if any of the following conditions are violated:
543///
544/// * `res` must point to a valid `AttestationResult` and has not been freed before.
545#[no_mangle]
546pub unsafe extern "C" fn AVmAttestationResult_free(res: *mut AttestationResult) {
547 if !res.is_null() {
548 // SAFETY: The result is only freed once is ensured by the caller.
549 let res = unsafe { Box::from_raw(res) };
550 drop(res)
551 }
552}
553
554/// Gets the path to the APK contents.
555#[no_mangle]
556pub extern "C" fn AVmPayload_getApkContentsPath() -> *const c_char {
557 VM_APK_CONTENTS_PATH_C.as_ptr()
558}
559
560/// Gets the path to the VM's encrypted storage.
561#[no_mangle]
562pub extern "C" fn AVmPayload_getEncryptedStoragePath() -> *const c_char {
563 if Path::new(ENCRYPTEDSTORE_MOUNTPOINT).exists() {
564 VM_ENCRYPTED_STORAGE_PATH_C.as_ptr()
565 } else {
566 ptr::null()
567 }
568}