blob: 14aff99991b85873e6f37558a1834086d295fa55 [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,
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +000019 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};
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +000026use log::{error, info, LevelFilter, debug};
Alice Wangc474a0f2024-02-06 13:11:57 +000027use rpcbinder::{RpcServer, RpcSession};
28use openssl::{ec::EcKey, sha::sha256, ecdsa::EcdsaSig};
29use std::convert::Infallible;
Chris Wailes73a1eb72025-01-15 11:56:43 -080030use std::ffi::CString;
Alice Wangc474a0f2024-02-06 13:11:57 +000031use 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;
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +000041use vm_payload_status_bindgen::AVmAccessRollbackProtectedSecretStatus::{AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND, AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED, AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_BAD_SIZE};
42use std::cmp::min;
Alice Wangc474a0f2024-02-06 13:11:57 +000043
Alice Wange64dd182024-01-17 15:57:55 +000044/// Maximum size of an ECDSA signature for EC P-256 key is 72 bytes.
45const MAX_ECDSA_P256_SIGNATURE_SIZE: usize = 72;
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +000046const RP_DATA_SIZE: usize = 32;
Alice Wange64dd182024-01-17 15:57:55 +000047
Andrew Walbran9c03a3a2024-09-03 12:12:59 +010048static VM_APK_CONTENTS_PATH_C: LazyLock<CString> =
49 LazyLock::new(|| CString::new(VM_APK_CONTENTS_PATH).expect("CString::new failed"));
50static PAYLOAD_CONNECTION: Mutex<Option<Strong<dyn IVmPayloadService>>> = Mutex::new(None);
51static VM_ENCRYPTED_STORAGE_PATH_C: LazyLock<CString> =
52 LazyLock::new(|| CString::new(ENCRYPTEDSTORE_MOUNTPOINT).expect("CString::new failed"));
Alice Wangc474a0f2024-02-06 13:11:57 +000053
54static 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.
58fn 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.
72fn 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.
82fn 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]
93pub 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.
105fn 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]
126pub 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`.
139unsafe 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) {
Devin Moore068e6742024-10-28 18:16:25 +0000150 Ok((server, _)) => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000151 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]
180pub 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
199fn 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]
223pub 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
237fn 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]
252pub 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
266fn 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.
Alice Wangc474a0f2024-02-06 13:11:57 +0000280///
281/// [valid]: ptr#safety
282#[no_mangle]
283pub unsafe extern "C" fn AVmPayload_requestAttestation(
284 challenge: *const u8,
285 challenge_size: usize,
286 res: &mut *mut AttestationResult,
Alice Wang1715f372024-02-14 10:51:52 +0000287) -> AVmAttestationStatus {
Alice Wange64dd182024-01-17 15:57:55 +0000288 // 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]
310pub unsafe extern "C" fn AVmPayload_requestAttestationForTesting(
311 challenge: *const u8,
312 challenge_size: usize,
313 res: &mut *mut AttestationResult,
Alice Wang1715f372024-02-14 10:51:52 +0000314) -> AVmAttestationStatus {
Alice Wange64dd182024-01-17 15:57:55 +0000315 // 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
336unsafe fn request_attestation(
337 challenge: *const u8,
338 challenge_size: usize,
339 test_mode: bool,
340 res: &mut *mut AttestationResult,
Alice Wang1715f372024-02-14 10:51:52 +0000341) -> AVmAttestationStatus {
Alice Wangc474a0f2024-02-06 13:11:57 +0000342 initialize_logging();
343 const MAX_CHALLENGE_SIZE: usize = 64;
344 if challenge_size > MAX_CHALLENGE_SIZE {
Alice Wang1715f372024-02-14 10:51:52 +0000345 return AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE;
Alice Wangc474a0f2024-02-06 13:11:57 +0000346 }
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());
Alice Wange64dd182024-01-17 15:57:55 +0000355 match service.requestAttestation(challenge, test_mode) {
Alice Wangc474a0f2024-02-06 13:11:57 +0000356 Ok(attestation_res) => {
357 *res = Box::into_raw(Box::new(attestation_res));
Alice Wang1715f372024-02-14 10:51:52 +0000358 AVmAttestationStatus::ATTESTATION_OK
Alice Wangc474a0f2024-02-06 13:11:57 +0000359 }
360 Err(e) => {
361 error!("Remote attestation failed: {e:?}");
362 binder_status_to_attestation_status(e)
363 }
364 }
365}
366
Alice Wang1715f372024-02-14 10:51:52 +0000367fn binder_status_to_attestation_status(status: binder::Status) -> AVmAttestationStatus {
Alice Wangc474a0f2024-02-06 13:11:57 +0000368 match status.exception_code() {
Alice Wang1715f372024-02-14 10:51:52 +0000369 ExceptionCode::UNSUPPORTED_OPERATION => AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED,
370 _ => AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED,
Alice Wangc474a0f2024-02-06 13:11:57 +0000371 }
372}
373
374/// Converts the return value from `AVmPayload_requestAttestation` to a text string
375/// representing the error code.
376#[no_mangle]
Alice Wang1715f372024-02-14 10:51:52 +0000377pub extern "C" fn AVmAttestationStatus_toString(status: AVmAttestationStatus) -> *const c_char {
Alice Wangc474a0f2024-02-06 13:11:57 +0000378 let message = match status {
Chris Wailes73a1eb72025-01-15 11:56:43 -0800379 AVmAttestationStatus::ATTESTATION_OK => c"The remote attestation completes successfully.",
Alice Wang1715f372024-02-14 10:51:52 +0000380 AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE => {
Chris Wailes73a1eb72025-01-15 11:56:43 -0800381 c"The challenge size is not between 0 and 64."
Alice Wangc474a0f2024-02-06 13:11:57 +0000382 }
Alice Wang1715f372024-02-14 10:51:52 +0000383 AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED => {
Chris Wailes73a1eb72025-01-15 11:56:43 -0800384 c"Failed to attest the VM. Please retry at a later time."
Alice Wangc474a0f2024-02-06 13:11:57 +0000385 }
Chris Wailes73a1eb72025-01-15 11:56:43 -0800386 AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => {
387 c"Remote attestation is not supported in the current environment."
388 }
Alice Wangc474a0f2024-02-06 13:11:57 +0000389 };
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.
Chris Wailes63b67d72024-08-19 16:23:21 -0700401/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
402/// memory `res` points to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000403///
404/// [valid]: ptr#safety
405/// [RFC 5915 s3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
406#[no_mangle]
407pub 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.
Chris Wailes63b67d72024-08-19 16:23:21 -0700439/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
440/// memory `res` or `message` point to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000441///
442///
443/// [valid]: ptr#safety
444#[no_mangle]
445pub 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 {
Alice Wange64dd182024-01-17 15:57:55 +0000452 // 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 }
Alice Wangc474a0f2024-02-06 13:11:57 +0000457 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));
Alice Wange64dd182024-01-17 15:57:55 +0000463 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()
Alice Wangc474a0f2024-02-06 13:11:57 +0000481 }
Alice Wangc474a0f2024-02-06 13:11:57 +0000482}
483
484fn 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]
493pub 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`.
Chris Wailes63b67d72024-08-19 16:23:21 -0700507/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
508/// memory `res` points to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000509///
510/// [valid]: ptr#safety
511#[no_mangle]
512pub 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]
545pub 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]
555pub 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]
561pub 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}
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +0000568
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]
582pub 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]
614pub 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
638fn 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
645fn 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}
Shikha Panwar5b7b4942024-12-18 15:32:49 +0000651
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]
656pub extern "C" fn AVmPayload_isNewInstance() -> bool {
657 unwrap_or_abort(try_is_new_instance())
658}
659
660fn try_is_new_instance() -> Result<bool> {
661 get_vm_payload_service()?.isNewInstance().context("Cannot determine if the instance is new")
662}