• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
15 //! This module handles the interaction with virtual machine payload service.
16 
17 use 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
20 };
21 use anyhow::{bail, ensure, Context, Result};
22 use binder::{
23     unstable_api::{new_spibinder, AIBinder},
24     Strong, ExceptionCode,
25 };
26 use log::{error, info, LevelFilter, debug};
27 use rpcbinder::{RpcServer, RpcSession};
28 use openssl::{ec::EcKey, sha::sha256, ecdsa::EcdsaSig};
29 use std::convert::Infallible;
30 use std::ffi::CString;
31 use std::fmt::Debug;
32 use std::os::raw::{c_char, c_void};
33 use std::path::Path;
34 use std::ptr::{self, NonNull};
35 use std::sync::{
36     atomic::{AtomicBool, Ordering},
37     LazyLock,
38     Mutex,
39 };
40 use vm_payload_status_bindgen::AVmAttestationStatus;
41 use vm_payload_status_bindgen::AVmAccessRollbackProtectedSecretStatus::{AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND, AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED, AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_BAD_SIZE};
42 use std::cmp::min;
43 
44 /// Maximum size of an ECDSA signature for EC P-256 key is 72 bytes.
45 const MAX_ECDSA_P256_SIGNATURE_SIZE: usize = 72;
46 const RP_DATA_SIZE: usize = 32;
47 
48 static VM_APK_CONTENTS_PATH_C: LazyLock<CString> =
49     LazyLock::new(|| CString::new(VM_APK_CONTENTS_PATH).expect("CString::new failed"));
50 static PAYLOAD_CONNECTION: Mutex<Option<Strong<dyn IVmPayloadService>>> = Mutex::new(None);
51 static VM_ENCRYPTED_STORAGE_PATH_C: LazyLock<CString> =
52     LazyLock::new(|| CString::new(ENCRYPTEDSTORE_MOUNTPOINT).expect("CString::new failed"));
53 
54 static ALREADY_NOTIFIED: AtomicBool = AtomicBool::new(false);
55 
56 /// Return a connection to the payload service in Microdroid Manager. Uses the existing connection
57 /// if there is one, otherwise attempts to create a new one.
get_vm_payload_service() -> Result<Strong<dyn IVmPayloadService>>58 fn get_vm_payload_service() -> Result<Strong<dyn IVmPayloadService>> {
59     let mut connection = PAYLOAD_CONNECTION.lock().unwrap();
60     if let Some(strong) = &*connection {
61         Ok(strong.clone())
62     } else {
63         let new_connection: Strong<dyn IVmPayloadService> = RpcSession::new()
64             .setup_unix_domain_client(VM_PAYLOAD_SERVICE_SOCKET_NAME)
65             .context(format!("Failed to connect to service: {}", VM_PAYLOAD_SERVICE_SOCKET_NAME))?;
66         *connection = Some(new_connection.clone());
67         Ok(new_connection)
68     }
69 }
70 
71 /// Make sure our logging goes to logcat. It is harmless to call this more than once.
initialize_logging()72 fn initialize_logging() {
73     android_logger::init_once(
74         android_logger::Config::default().with_tag("vm_payload").with_max_level(LevelFilter::Info),
75     );
76 }
77 
78 /// In many cases clients can't do anything useful if API calls fail, and the failure
79 /// generally indicates that the VM is exiting or otherwise doomed. So rather than
80 /// returning a non-actionable error indication we just log the problem and abort
81 /// the process.
unwrap_or_abort<T, E: Debug>(result: Result<T, E>) -> T82 fn unwrap_or_abort<T, E: Debug>(result: Result<T, E>) -> T {
83     result.unwrap_or_else(|e| {
84         let msg = format!("{:?}", e);
85         error!("{msg}");
86         panic!("{msg}")
87     })
88 }
89 
90 /// Notifies the host that the payload is ready.
91 /// Panics on failure.
92 #[no_mangle]
AVmPayload_notifyPayloadReady()93 pub extern "C" fn AVmPayload_notifyPayloadReady() {
94     initialize_logging();
95 
96     if !ALREADY_NOTIFIED.swap(true, Ordering::Relaxed) {
97         unwrap_or_abort(try_notify_payload_ready());
98 
99         info!("Notified host payload ready successfully");
100     }
101 }
102 
103 /// Notifies the host that the payload is ready.
104 /// Returns a `Result` containing error information if failed.
try_notify_payload_ready() -> Result<()>105 fn try_notify_payload_ready() -> Result<()> {
106     get_vm_payload_service()?.notifyPayloadReady().context("Cannot notify payload ready")
107 }
108 
109 /// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
110 /// port.
111 ///
112 /// If and when the server is ready for connections (it is listening on the port), `on_ready` is
113 /// called to allow appropriate action to be taken - e.g. to notify clients that they may now
114 /// attempt to connect.
115 ///
116 /// The current thread joins the binder thread pool to handle incoming messages.
117 /// This function never returns.
118 ///
119 /// Panics on error (including unexpected server exit).
120 ///
121 /// # Safety
122 ///
123 /// If present, the `on_ready` callback must be a valid function pointer, which will be called at
124 /// most once, while this function is executing, with the `param` parameter.
125 #[no_mangle]
AVmPayload_runVsockRpcServer( service: *mut AIBinder, port: u32, on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>, param: *mut c_void, ) -> Infallible126 pub unsafe extern "C" fn AVmPayload_runVsockRpcServer(
127     service: *mut AIBinder,
128     port: u32,
129     on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>,
130     param: *mut c_void,
131 ) -> Infallible {
132     initialize_logging();
133 
134     // SAFETY: try_run_vsock_server has the same requirements as this function
135     unwrap_or_abort(unsafe { try_run_vsock_server(service, port, on_ready, param) })
136 }
137 
138 /// # Safety: Same as `AVmPayload_runVsockRpcServer`.
try_run_vsock_server( service: *mut AIBinder, port: u32, on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>, param: *mut c_void, ) -> Result<Infallible>139 unsafe fn try_run_vsock_server(
140     service: *mut AIBinder,
141     port: u32,
142     on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>,
143     param: *mut c_void,
144 ) -> Result<Infallible> {
145     // SAFETY: AIBinder returned has correct reference count, and the ownership can
146     // safely be taken by new_spibinder.
147     let service = unsafe { new_spibinder(service) };
148     if let Some(service) = service {
149         match RpcServer::new_vsock(service, libc::VMADDR_CID_HOST, port) {
150             Ok((server, _)) => {
151                 if let Some(on_ready) = on_ready {
152                     // SAFETY: We're calling the callback with the parameter specified within the
153                     // allowed lifetime.
154                     unsafe { on_ready(param) };
155                 }
156                 server.join();
157                 bail!("RpcServer unexpectedly terminated");
158             }
159             Err(err) => {
160                 bail!("Failed to start RpcServer: {:?}", err);
161             }
162         }
163     } else {
164         bail!("Failed to convert the given service from AIBinder to SpIBinder.");
165     }
166 }
167 
168 /// Get a secret that is uniquely bound to this VM instance.
169 /// Panics on failure.
170 ///
171 /// # Safety
172 ///
173 /// Behavior is undefined if any of the following conditions are violated:
174 ///
175 /// * `identifier` must be [valid] for reads of `identifier_size` bytes.
176 /// * `secret` must be [valid] for writes of `size` bytes.
177 ///
178 /// [valid]: ptr#safety
179 #[no_mangle]
AVmPayload_getVmInstanceSecret( identifier: *const u8, identifier_size: usize, secret: *mut u8, size: usize, )180 pub unsafe extern "C" fn AVmPayload_getVmInstanceSecret(
181     identifier: *const u8,
182     identifier_size: usize,
183     secret: *mut u8,
184     size: usize,
185 ) {
186     initialize_logging();
187 
188     // SAFETY: See the requirements on `identifier` above.
189     let identifier = unsafe { std::slice::from_raw_parts(identifier, identifier_size) };
190     let vm_secret = unwrap_or_abort(try_get_vm_instance_secret(identifier, size));
191 
192     // SAFETY: See the requirements on `secret` above; `vm_secret` is known to have length `size`,
193     // and cannot overlap `secret` because we just allocated it.
194     unsafe {
195         ptr::copy_nonoverlapping(vm_secret.as_ptr(), secret, size);
196     }
197 }
198 
try_get_vm_instance_secret(identifier: &[u8], size: usize) -> Result<Vec<u8>>199 fn try_get_vm_instance_secret(identifier: &[u8], size: usize) -> Result<Vec<u8>> {
200     let vm_secret = get_vm_payload_service()?
201         .getVmInstanceSecret(identifier, i32::try_from(size)?)
202         .context("Cannot get VM instance secret")?;
203     ensure!(
204         vm_secret.len() == size,
205         "Returned secret has {} bytes, expected {}",
206         vm_secret.len(),
207         size
208     );
209     Ok(vm_secret)
210 }
211 
212 /// Get the VM's attestation chain.
213 /// Panics on failure.
214 ///
215 /// # Safety
216 ///
217 /// Behavior is undefined if any of the following conditions are violated:
218 ///
219 /// * `data` must be [valid] for writes of `size` bytes, if size > 0.
220 ///
221 /// [valid]: ptr#safety
222 #[no_mangle]
AVmPayload_getDiceAttestationChain(data: *mut u8, size: usize) -> usize223 pub unsafe extern "C" fn AVmPayload_getDiceAttestationChain(data: *mut u8, size: usize) -> usize {
224     initialize_logging();
225 
226     let chain = unwrap_or_abort(try_get_dice_attestation_chain());
227     if size != 0 {
228         // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
229         // the length of either buffer, and `chain` cannot overlap `data` because we just allocated
230         // it. We allow data to be null, which is never valid, but only if size == 0 which is
231         // checked above.
232         unsafe { ptr::copy_nonoverlapping(chain.as_ptr(), data, std::cmp::min(chain.len(), size)) };
233     }
234     chain.len()
235 }
236 
try_get_dice_attestation_chain() -> Result<Vec<u8>>237 fn try_get_dice_attestation_chain() -> Result<Vec<u8>> {
238     get_vm_payload_service()?.getDiceAttestationChain().context("Cannot get attestation chain")
239 }
240 
241 /// Get the VM's attestation CDI.
242 /// Panics on failure.
243 ///
244 /// # Safety
245 ///
246 /// Behavior is undefined if any of the following conditions are violated:
247 ///
248 /// * `data` must be [valid] for writes of `size` bytes, if size > 0.
249 ///
250 /// [valid]: ptr#safety
251 #[no_mangle]
AVmPayload_getDiceAttestationCdi(data: *mut u8, size: usize) -> usize252 pub unsafe extern "C" fn AVmPayload_getDiceAttestationCdi(data: *mut u8, size: usize) -> usize {
253     initialize_logging();
254 
255     let cdi = unwrap_or_abort(try_get_dice_attestation_cdi());
256     if size != 0 {
257         // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
258         // the length of either buffer, and `cdi` cannot overlap `data` because we just allocated
259         // it. We allow data to be null, which is never valid, but only if size == 0 which is
260         // checked above.
261         unsafe { ptr::copy_nonoverlapping(cdi.as_ptr(), data, std::cmp::min(cdi.len(), size)) };
262     }
263     cdi.len()
264 }
265 
try_get_dice_attestation_cdi() -> Result<Vec<u8>>266 fn try_get_dice_attestation_cdi() -> Result<Vec<u8>> {
267     get_vm_payload_service()?.getDiceAttestationCdi().context("Cannot get attestation CDI")
268 }
269 
270 /// Requests the remote attestation of the client VM.
271 ///
272 /// The challenge will be included in the certificate chain in the attestation result,
273 /// serving as proof of the freshness of the result.
274 ///
275 /// # Safety
276 ///
277 /// Behavior is undefined if any of the following conditions are violated:
278 ///
279 /// * `challenge` must be [valid] for reads of `challenge_size` bytes.
280 ///
281 /// [valid]: ptr#safety
282 #[no_mangle]
AVmPayload_requestAttestation( challenge: *const u8, challenge_size: usize, res: &mut *mut AttestationResult, ) -> AVmAttestationStatus283 pub unsafe extern "C" fn AVmPayload_requestAttestation(
284     challenge: *const u8,
285     challenge_size: usize,
286     res: &mut *mut AttestationResult,
287 ) -> AVmAttestationStatus {
288     // SAFETY: The caller guarantees that `challenge` is valid for reads and `res` is valid
289     // for writes.
290     unsafe {
291         request_attestation(
292             challenge,
293             challenge_size,
294             false, // test_mode
295             res,
296         )
297     }
298 }
299 
300 /// Requests the remote attestation of the client VM for testing.
301 ///
302 /// # Safety
303 ///
304 /// Behavior is undefined if any of the following conditions are violated:
305 ///
306 /// * `challenge` must be [valid] for reads of `challenge_size` bytes.
307 ///
308 /// [valid]: ptr#safety
309 #[no_mangle]
AVmPayload_requestAttestationForTesting( challenge: *const u8, challenge_size: usize, res: &mut *mut AttestationResult, ) -> AVmAttestationStatus310 pub unsafe extern "C" fn AVmPayload_requestAttestationForTesting(
311     challenge: *const u8,
312     challenge_size: usize,
313     res: &mut *mut AttestationResult,
314 ) -> AVmAttestationStatus {
315     // SAFETY: The caller guarantees that `challenge` is valid for reads and `res` is valid
316     // for writes.
317     unsafe {
318         request_attestation(
319             challenge,
320             challenge_size,
321             true, // test_mode
322             res,
323         )
324     }
325 }
326 
327 /// Requests the remote attestation of the client VM.
328 ///
329 /// # Safety
330 ///
331 /// Behavior is undefined if any of the following conditions are violated:
332 ///
333 /// * `challenge` must be [valid] for reads of `challenge_size` bytes.
334 ///
335 /// [valid]: ptr#safety
request_attestation( challenge: *const u8, challenge_size: usize, test_mode: bool, res: &mut *mut AttestationResult, ) -> AVmAttestationStatus336 unsafe fn request_attestation(
337     challenge: *const u8,
338     challenge_size: usize,
339     test_mode: bool,
340     res: &mut *mut AttestationResult,
341 ) -> AVmAttestationStatus {
342     initialize_logging();
343     const MAX_CHALLENGE_SIZE: usize = 64;
344     if challenge_size > MAX_CHALLENGE_SIZE {
345         return AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE;
346     }
347     let challenge = if challenge_size == 0 {
348         &[]
349     } else {
350         // SAFETY: The caller guarantees that `challenge` is valid for reads of
351         // `challenge_size` bytes and `challenge_size` is not zero.
352         unsafe { std::slice::from_raw_parts(challenge, challenge_size) }
353     };
354     let service = unwrap_or_abort(get_vm_payload_service());
355     match service.requestAttestation(challenge, test_mode) {
356         Ok(attestation_res) => {
357             *res = Box::into_raw(Box::new(attestation_res));
358             AVmAttestationStatus::ATTESTATION_OK
359         }
360         Err(e) => {
361             error!("Remote attestation failed: {e:?}");
362             binder_status_to_attestation_status(e)
363         }
364     }
365 }
366 
binder_status_to_attestation_status(status: binder::Status) -> AVmAttestationStatus367 fn binder_status_to_attestation_status(status: binder::Status) -> AVmAttestationStatus {
368     match status.exception_code() {
369         ExceptionCode::UNSUPPORTED_OPERATION => AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED,
370         _ => AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED,
371     }
372 }
373 
374 /// Converts the return value from `AVmPayload_requestAttestation` to a text string
375 /// representing the error code.
376 #[no_mangle]
AVmAttestationStatus_toString(status: AVmAttestationStatus) -> *const c_char377 pub extern "C" fn AVmAttestationStatus_toString(status: AVmAttestationStatus) -> *const c_char {
378     let message = match status {
379         AVmAttestationStatus::ATTESTATION_OK => c"The remote attestation completes successfully.",
380         AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE => {
381             c"The challenge size is not between 0 and 64."
382         }
383         AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED => {
384             c"Failed to attest the VM. Please retry at a later time."
385         }
386         AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => {
387             c"Remote attestation is not supported in the current environment."
388         }
389     };
390     message.as_ptr()
391 }
392 
393 /// Reads the DER-encoded ECPrivateKey structure specified in [RFC 5915 s3] for the
394 /// EC P-256 private key from the provided attestation result.
395 ///
396 /// # Safety
397 ///
398 /// Behavior is undefined if any of the following conditions are violated:
399 ///
400 /// * `data` must be [valid] for writes of `size` bytes, if size > 0.
401 /// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
402 ///   memory `res` points to.
403 ///
404 /// [valid]: ptr#safety
405 /// [RFC 5915 s3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
406 #[no_mangle]
AVmAttestationResult_getPrivateKey( res: &AttestationResult, data: *mut u8, size: usize, ) -> usize407 pub unsafe extern "C" fn AVmAttestationResult_getPrivateKey(
408     res: &AttestationResult,
409     data: *mut u8,
410     size: usize,
411 ) -> usize {
412     let private_key = &res.privateKey;
413     if size != 0 {
414         let data = NonNull::new(data).expect("data must not be null when size > 0");
415         // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
416         // the length of either buffer, and the caller ensures that `private_key` cannot overlap
417         // `data`. We allow data to be null, which is never valid, but only if size == 0
418         // which is checked above.
419         unsafe {
420             ptr::copy_nonoverlapping(
421                 private_key.as_ptr(),
422                 data.as_ptr(),
423                 std::cmp::min(private_key.len(), size),
424             )
425         };
426     }
427     private_key.len()
428 }
429 
430 /// Signs the given message using ECDSA P-256, the message is first hashed with SHA-256 and
431 /// then it is signed with the attested EC P-256 private key in the attestation result.
432 ///
433 /// # Safety
434 ///
435 /// Behavior is undefined if any of the following conditions are violated:
436 ///
437 /// * `message` must be [valid] for reads of `message_size` bytes.
438 /// * `data` must be [valid] for writes of `size` bytes, if size > 0.
439 /// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
440 ///   memory `res` or `message` point to.
441 ///
442 ///
443 /// [valid]: ptr#safety
444 #[no_mangle]
AVmAttestationResult_sign( res: &AttestationResult, message: *const u8, message_size: usize, data: *mut u8, size: usize, ) -> usize445 pub unsafe extern "C" fn AVmAttestationResult_sign(
446     res: &AttestationResult,
447     message: *const u8,
448     message_size: usize,
449     data: *mut u8,
450     size: usize,
451 ) -> usize {
452     // A DER-encoded ECDSA signature can have varying sizes even with the same EC Key and message,
453     // due to the encoding of the random values r and s that are part of the signature.
454     if size == 0 {
455         return MAX_ECDSA_P256_SIGNATURE_SIZE;
456     }
457     if message_size == 0 {
458         panic!("Message to be signed must not be empty.")
459     }
460     // SAFETY: See the requirements on `message` above.
461     let message = unsafe { std::slice::from_raw_parts(message, message_size) };
462     let signature = unwrap_or_abort(try_ecdsa_sign(message, &res.privateKey));
463     let data = NonNull::new(data).expect("data must not be null when size > 0");
464     // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
465     // the length of either buffer, and the caller ensures that `signature` cannot overlap
466     // `data`. We allow data to be null, which is never valid, but only if size == 0
467     // which is checked above.
468     unsafe {
469         ptr::copy_nonoverlapping(
470             signature.as_ptr(),
471             data.as_ptr(),
472             usize::min(signature.len(), size),
473         )
474     };
475     if size < signature.len() {
476         // If the buffer is too small, return the maximum size of the signature to allow the caller
477         // to allocate a buffer large enough to call this function again.
478         MAX_ECDSA_P256_SIGNATURE_SIZE
479     } else {
480         signature.len()
481     }
482 }
483 
try_ecdsa_sign(message: &[u8], der_encoded_ec_private_key: &[u8]) -> Result<Vec<u8>>484 fn try_ecdsa_sign(message: &[u8], der_encoded_ec_private_key: &[u8]) -> Result<Vec<u8>> {
485     let private_key = EcKey::private_key_from_der(der_encoded_ec_private_key)?;
486     let digest = sha256(message);
487     let sig = EcdsaSig::sign(&digest, &private_key)?;
488     Ok(sig.to_der()?)
489 }
490 
491 /// Gets the number of certificates in the certificate chain.
492 #[no_mangle]
AVmAttestationResult_getCertificateCount(res: &AttestationResult) -> usize493 pub extern "C" fn AVmAttestationResult_getCertificateCount(res: &AttestationResult) -> usize {
494     res.certificateChain.len()
495 }
496 
497 /// Retrieves the certificate at the given `index` from the certificate chain in the provided
498 /// attestation result.
499 ///
500 /// # Safety
501 ///
502 /// Behavior is undefined if any of the following conditions are violated:
503 ///
504 /// * `data` must be [valid] for writes of `size` bytes, if size > 0.
505 /// * `index` must be within the range of [0, number of certificates). The number of certificates
506 ///   can be obtained with `AVmAttestationResult_getCertificateCount`.
507 /// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
508 ///   memory `res` points to.
509 ///
510 /// [valid]: ptr#safety
511 #[no_mangle]
AVmAttestationResult_getCertificateAt( res: &AttestationResult, index: usize, data: *mut u8, size: usize, ) -> usize512 pub unsafe extern "C" fn AVmAttestationResult_getCertificateAt(
513     res: &AttestationResult,
514     index: usize,
515     data: *mut u8,
516     size: usize,
517 ) -> usize {
518     let certificate =
519         &res.certificateChain.get(index).expect("The index is out of bounds.").encodedCertificate;
520     if size != 0 {
521         let data = NonNull::new(data).expect("data must not be null when size > 0");
522         // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
523         // the length of either buffer, and the caller ensures that `certificate` cannot overlap
524         // `data`. We allow data to be null, which is never valid, but only if size == 0
525         // which is checked above.
526         unsafe {
527             ptr::copy_nonoverlapping(
528                 certificate.as_ptr(),
529                 data.as_ptr(),
530                 std::cmp::min(certificate.len(), size),
531             )
532         };
533     }
534     certificate.len()
535 }
536 
537 /// Frees all the data owned by given attestation result and result itself.
538 ///
539 /// # Safety
540 ///
541 /// Behavior is undefined if any of the following conditions are violated:
542 ///
543 /// * `res` must point to a valid `AttestationResult` and has not been freed before.
544 #[no_mangle]
AVmAttestationResult_free(res: *mut AttestationResult)545 pub unsafe extern "C" fn AVmAttestationResult_free(res: *mut AttestationResult) {
546     if !res.is_null() {
547         // SAFETY: The result is only freed once is ensured by the caller.
548         let res = unsafe { Box::from_raw(res) };
549         drop(res)
550     }
551 }
552 
553 /// Gets the path to the APK contents.
554 #[no_mangle]
AVmPayload_getApkContentsPath() -> *const c_char555 pub extern "C" fn AVmPayload_getApkContentsPath() -> *const c_char {
556     VM_APK_CONTENTS_PATH_C.as_ptr()
557 }
558 
559 /// Gets the path to the VM's encrypted storage.
560 #[no_mangle]
AVmPayload_getEncryptedStoragePath() -> *const c_char561 pub extern "C" fn AVmPayload_getEncryptedStoragePath() -> *const c_char {
562     if Path::new(ENCRYPTEDSTORE_MOUNTPOINT).exists() {
563         VM_ENCRYPTED_STORAGE_PATH_C.as_ptr()
564     } else {
565         ptr::null()
566     }
567 }
568 
569 /// Writes up to n bytes from buffer starting at `buf`, on behalf of the payload, to rollback
570 /// detectable storage and return the number of bytes written or appropriate (negative) status.
571 /// For this implementation, the backing storage is Secretkeeper HAL, which allows storing & reading
572 /// of 32 bytes secret!
573 ///
574 /// # Safety
575 ///
576 /// Behavior is undefined if any of the following conditions are violated:
577 ///
578 /// * `buf` must be [valid] for reads of n bytes.
579 ///
580 /// [valid]: ptr#safety
581 #[no_mangle]
AVmPayload_writeRollbackProtectedSecret(buf: *const u8, n: usize) -> i32582 pub unsafe extern "C" fn AVmPayload_writeRollbackProtectedSecret(buf: *const u8, n: usize) -> i32 {
583     initialize_logging();
584     if n < RP_DATA_SIZE {
585         error!(
586             "Requested writing {} bytes, while Secretkeeper supports only {} bytes",
587             n, RP_DATA_SIZE
588         );
589         return AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_BAD_SIZE as i32;
590     }
591     // Safety: See the requirements on `buf` above and we just checked that n >= RP_DATA_SIZE.
592     let buf = unsafe { std::slice::from_raw_parts(buf, RP_DATA_SIZE) };
593     match try_writing_payload_rollback_protected_data(buf.try_into().unwrap()) {
594         Ok(()) => RP_DATA_SIZE as i32,
595         Err(e) => {
596             error!("Failed to write rollback protected data: {e:?}");
597             AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED as i32
598         }
599     }
600 }
601 
602 /// Read up to n bytes of payload's data in rollback detectable storage into `buf`.
603 /// For this implementation, the backing storage is Secretkeeper HAL, which allows storing & reading
604 /// of 32 bytes secret!
605 ///
606 /// # Safety
607 ///
608 /// Behavior is undefined if any of the following conditions are violated:
609 ///
610 /// * `buf` must be [valid] for writes of n bytes.
611 ///
612 /// [valid]: ptr#safety
613 #[no_mangle]
AVmPayload_readRollbackProtectedSecret(buf: *mut u8, n: usize) -> i32614 pub unsafe extern "C" fn AVmPayload_readRollbackProtectedSecret(buf: *mut u8, n: usize) -> i32 {
615     initialize_logging();
616     match try_read_rollback_protected_data() {
617         Err(e) => {
618             error!("Failed to read rollback protected data: {e:?}");
619             AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED as i32
620         }
621         Ok(stored_data) => {
622             if let Some(stored_data) = stored_data {
623                 // SAFETY: See the requirements on `buf` above; `stored_data` is known to have
624                 // length `RP_DATA_SIZE`, and cannot overlap `data` because we just allocated
625                 // it.
626                 unsafe {
627                     ptr::copy_nonoverlapping(stored_data.as_ptr(), buf, min(n, RP_DATA_SIZE));
628                 }
629                 RP_DATA_SIZE as i32
630             } else {
631                 debug!("No relevant entry found in Secretkeeper");
632                 AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND as i32
633             }
634         }
635     }
636 }
637 
try_writing_payload_rollback_protected_data(data: &[u8; RP_DATA_SIZE]) -> Result<()>638 fn try_writing_payload_rollback_protected_data(data: &[u8; RP_DATA_SIZE]) -> Result<()> {
639     get_vm_payload_service()?
640         .writePayloadRpData(data)
641         .context("Failed to write payload rollback protected data")?;
642     Ok(())
643 }
644 
try_read_rollback_protected_data() -> Result<Option<[u8; RP_DATA_SIZE]>>645 fn try_read_rollback_protected_data() -> Result<Option<[u8; RP_DATA_SIZE]>> {
646     let rp = get_vm_payload_service()?
647         .readPayloadRpData()
648         .context("Failed to read rollback protected data")?;
649     Ok(rp)
650 }
651 
652 /// Checks whether the VM instance is new - i.e., if this is the first run of an instance.
653 ///
654 /// Panics on error (including unexpected server exit).
655 #[no_mangle]
AVmPayload_isNewInstance() -> bool656 pub extern "C" fn AVmPayload_isNewInstance() -> bool {
657     unwrap_or_abort(try_is_new_instance())
658 }
659 
try_is_new_instance() -> Result<bool>660 fn try_is_new_instance() -> Result<bool> {
661     get_vm_payload_service()?.isNewInstance().context("Cannot determine if the instance is new")
662 }
663