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