blob: cbadec210f12824fcde5318f68474647549c431d [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;
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;
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 {
Alice Wang1715f372024-02-14 10:51:52 +0000379 AVmAttestationStatus::ATTESTATION_OK => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000380 CStr::from_bytes_with_nul(b"The remote attestation completes successfully.\0").unwrap()
381 }
Alice Wang1715f372024-02-14 10:51:52 +0000382 AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000383 CStr::from_bytes_with_nul(b"The challenge size is not between 0 and 64.\0").unwrap()
384 }
Alice Wang1715f372024-02-14 10:51:52 +0000385 AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED => {
Alice Wangc474a0f2024-02-06 13:11:57 +0000386 CStr::from_bytes_with_nul(b"Failed to attest the VM. Please retry at a later time.\0")
387 .unwrap()
388 }
Alice Wang1715f372024-02-14 10:51:52 +0000389 AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => CStr::from_bytes_with_nul(
Alice Wangc474a0f2024-02-06 13:11:57 +0000390 b"Remote attestation is not supported in the current environment.\0",
391 )
392 .unwrap(),
393 };
394 message.as_ptr()
395}
396
397/// Reads the DER-encoded ECPrivateKey structure specified in [RFC 5915 s3] for the
398/// EC P-256 private key from the provided attestation result.
399///
400/// # Safety
401///
402/// Behavior is undefined if any of the following conditions are violated:
403///
404/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
Chris Wailes63b67d72024-08-19 16:23:21 -0700405/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
406/// memory `res` points to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000407///
408/// [valid]: ptr#safety
409/// [RFC 5915 s3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
410#[no_mangle]
411pub unsafe extern "C" fn AVmAttestationResult_getPrivateKey(
412 res: &AttestationResult,
413 data: *mut u8,
414 size: usize,
415) -> usize {
416 let private_key = &res.privateKey;
417 if size != 0 {
418 let data = NonNull::new(data).expect("data must not be null when size > 0");
419 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
420 // the length of either buffer, and the caller ensures that `private_key` cannot overlap
421 // `data`. We allow data to be null, which is never valid, but only if size == 0
422 // which is checked above.
423 unsafe {
424 ptr::copy_nonoverlapping(
425 private_key.as_ptr(),
426 data.as_ptr(),
427 std::cmp::min(private_key.len(), size),
428 )
429 };
430 }
431 private_key.len()
432}
433
434/// Signs the given message using ECDSA P-256, the message is first hashed with SHA-256 and
435/// then it is signed with the attested EC P-256 private key in the attestation result.
436///
437/// # Safety
438///
439/// Behavior is undefined if any of the following conditions are violated:
440///
441/// * `message` must be [valid] for reads of `message_size` bytes.
442/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
Chris Wailes63b67d72024-08-19 16:23:21 -0700443/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
444/// memory `res` or `message` point to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000445///
446///
447/// [valid]: ptr#safety
448#[no_mangle]
449pub unsafe extern "C" fn AVmAttestationResult_sign(
450 res: &AttestationResult,
451 message: *const u8,
452 message_size: usize,
453 data: *mut u8,
454 size: usize,
455) -> usize {
Alice Wange64dd182024-01-17 15:57:55 +0000456 // A DER-encoded ECDSA signature can have varying sizes even with the same EC Key and message,
457 // due to the encoding of the random values r and s that are part of the signature.
458 if size == 0 {
459 return MAX_ECDSA_P256_SIGNATURE_SIZE;
460 }
Alice Wangc474a0f2024-02-06 13:11:57 +0000461 if message_size == 0 {
462 panic!("Message to be signed must not be empty.")
463 }
464 // SAFETY: See the requirements on `message` above.
465 let message = unsafe { std::slice::from_raw_parts(message, message_size) };
466 let signature = unwrap_or_abort(try_ecdsa_sign(message, &res.privateKey));
Alice Wange64dd182024-01-17 15:57:55 +0000467 let data = NonNull::new(data).expect("data must not be null when size > 0");
468 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
469 // the length of either buffer, and the caller ensures that `signature` cannot overlap
470 // `data`. We allow data to be null, which is never valid, but only if size == 0
471 // which is checked above.
472 unsafe {
473 ptr::copy_nonoverlapping(
474 signature.as_ptr(),
475 data.as_ptr(),
476 usize::min(signature.len(), size),
477 )
478 };
479 if size < signature.len() {
480 // If the buffer is too small, return the maximum size of the signature to allow the caller
481 // to allocate a buffer large enough to call this function again.
482 MAX_ECDSA_P256_SIGNATURE_SIZE
483 } else {
484 signature.len()
Alice Wangc474a0f2024-02-06 13:11:57 +0000485 }
Alice Wangc474a0f2024-02-06 13:11:57 +0000486}
487
488fn try_ecdsa_sign(message: &[u8], der_encoded_ec_private_key: &[u8]) -> Result<Vec<u8>> {
489 let private_key = EcKey::private_key_from_der(der_encoded_ec_private_key)?;
490 let digest = sha256(message);
491 let sig = EcdsaSig::sign(&digest, &private_key)?;
492 Ok(sig.to_der()?)
493}
494
495/// Gets the number of certificates in the certificate chain.
496#[no_mangle]
497pub extern "C" fn AVmAttestationResult_getCertificateCount(res: &AttestationResult) -> usize {
498 res.certificateChain.len()
499}
500
501/// Retrieves the certificate at the given `index` from the certificate chain in the provided
502/// attestation result.
503///
504/// # Safety
505///
506/// Behavior is undefined if any of the following conditions are violated:
507///
508/// * `data` must be [valid] for writes of `size` bytes, if size > 0.
509/// * `index` must be within the range of [0, number of certificates). The number of certificates
510/// can be obtained with `AVmAttestationResult_getCertificateCount`.
Chris Wailes63b67d72024-08-19 16:23:21 -0700511/// * The region of memory beginning at `data` with `size` bytes must not overlap with the region of
512/// memory `res` points to.
Alice Wangc474a0f2024-02-06 13:11:57 +0000513///
514/// [valid]: ptr#safety
515#[no_mangle]
516pub unsafe extern "C" fn AVmAttestationResult_getCertificateAt(
517 res: &AttestationResult,
518 index: usize,
519 data: *mut u8,
520 size: usize,
521) -> usize {
522 let certificate =
523 &res.certificateChain.get(index).expect("The index is out of bounds.").encodedCertificate;
524 if size != 0 {
525 let data = NonNull::new(data).expect("data must not be null when size > 0");
526 // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
527 // the length of either buffer, and the caller ensures that `certificate` cannot overlap
528 // `data`. We allow data to be null, which is never valid, but only if size == 0
529 // which is checked above.
530 unsafe {
531 ptr::copy_nonoverlapping(
532 certificate.as_ptr(),
533 data.as_ptr(),
534 std::cmp::min(certificate.len(), size),
535 )
536 };
537 }
538 certificate.len()
539}
540
541/// Frees all the data owned by given attestation result and result itself.
542///
543/// # Safety
544///
545/// Behavior is undefined if any of the following conditions are violated:
546///
547/// * `res` must point to a valid `AttestationResult` and has not been freed before.
548#[no_mangle]
549pub unsafe extern "C" fn AVmAttestationResult_free(res: *mut AttestationResult) {
550 if !res.is_null() {
551 // SAFETY: The result is only freed once is ensured by the caller.
552 let res = unsafe { Box::from_raw(res) };
553 drop(res)
554 }
555}
556
557/// Gets the path to the APK contents.
558#[no_mangle]
559pub extern "C" fn AVmPayload_getApkContentsPath() -> *const c_char {
560 VM_APK_CONTENTS_PATH_C.as_ptr()
561}
562
563/// Gets the path to the VM's encrypted storage.
564#[no_mangle]
565pub extern "C" fn AVmPayload_getEncryptedStoragePath() -> *const c_char {
566 if Path::new(ENCRYPTEDSTORE_MOUNTPOINT).exists() {
567 VM_ENCRYPTED_STORAGE_PATH_C.as_ptr()
568 } else {
569 ptr::null()
570 }
571}
Shikha Panwar0c3a2fa2024-12-06 18:38:06 +0000572
573/// Writes up to n bytes from buffer starting at `buf`, on behalf of the payload, to rollback
574/// detectable storage and return the number of bytes written or appropriate (negative) status.
575/// For this implementation, the backing storage is Secretkeeper HAL, which allows storing & reading
576/// of 32 bytes secret!
577///
578/// # Safety
579///
580/// Behavior is undefined if any of the following conditions are violated:
581///
582/// * `buf` must be [valid] for reads of n bytes.
583///
584/// [valid]: ptr#safety
585#[no_mangle]
586pub unsafe extern "C" fn AVmPayload_writeRollbackProtectedSecret(buf: *const u8, n: usize) -> i32 {
587 initialize_logging();
588 if n < RP_DATA_SIZE {
589 error!(
590 "Requested writing {} bytes, while Secretkeeper supports only {} bytes",
591 n, RP_DATA_SIZE
592 );
593 return AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_BAD_SIZE as i32;
594 }
595 // Safety: See the requirements on `buf` above and we just checked that n >= RP_DATA_SIZE.
596 let buf = unsafe { std::slice::from_raw_parts(buf, RP_DATA_SIZE) };
597 match try_writing_payload_rollback_protected_data(buf.try_into().unwrap()) {
598 Ok(()) => RP_DATA_SIZE as i32,
599 Err(e) => {
600 error!("Failed to write rollback protected data: {e:?}");
601 AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED as i32
602 }
603 }
604}
605
606/// Read up to n bytes of payload's data in rollback detectable storage into `buf`.
607/// For this implementation, the backing storage is Secretkeeper HAL, which allows storing & reading
608/// of 32 bytes secret!
609///
610/// # Safety
611///
612/// Behavior is undefined if any of the following conditions are violated:
613///
614/// * `buf` must be [valid] for writes of n bytes.
615///
616/// [valid]: ptr#safety
617#[no_mangle]
618pub unsafe extern "C" fn AVmPayload_readRollbackProtectedSecret(buf: *mut u8, n: usize) -> i32 {
619 initialize_logging();
620 match try_read_rollback_protected_data() {
621 Err(e) => {
622 error!("Failed to read rollback protected data: {e:?}");
623 AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED as i32
624 }
625 Ok(stored_data) => {
626 if let Some(stored_data) = stored_data {
627 // SAFETY: See the requirements on `buf` above; `stored_data` is known to have
628 // length `RP_DATA_SIZE`, and cannot overlap `data` because we just allocated
629 // it.
630 unsafe {
631 ptr::copy_nonoverlapping(stored_data.as_ptr(), buf, min(n, RP_DATA_SIZE));
632 }
633 RP_DATA_SIZE as i32
634 } else {
635 debug!("No relevant entry found in Secretkeeper");
636 AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND as i32
637 }
638 }
639 }
640}
641
642fn try_writing_payload_rollback_protected_data(data: &[u8; RP_DATA_SIZE]) -> Result<()> {
643 get_vm_payload_service()?
644 .writePayloadRpData(data)
645 .context("Failed to write payload rollback protected data")?;
646 Ok(())
647}
648
649fn try_read_rollback_protected_data() -> Result<Option<[u8; RP_DATA_SIZE]>> {
650 let rp = get_vm_payload_service()?
651 .readPayloadRpData()
652 .context("Failed to read rollback protected data")?;
653 Ok(rp)
654}
Shikha Panwar5b7b4942024-12-18 15:32:49 +0000655
656/// Checks whether the VM instance is new - i.e., if this is the first run of an instance.
657///
658/// Panics on error (including unexpected server exit).
659#[no_mangle]
660pub extern "C" fn AVmPayload_isNewInstance() -> bool {
661 unwrap_or_abort(try_is_new_instance())
662}
663
664fn try_is_new_instance() -> Result<bool> {
665 get_vm_payload_service()?.isNewInstance().context("Cannot determine if the instance is new")
666}