1 // Copyright 2023, 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 //! Main executable of Service VM client for manual testing.
16 
17 use anyhow::{anyhow, ensure, Result};
18 use log::{error, info};
19 use std::{
20     ffi::{c_void, CStr},
21     panic,
22     ptr::{self, NonNull},
23     result,
24 };
25 use vm_payload_bindgen::{
26     AVmAttestationResult, AVmAttestationResult_free, AVmAttestationResult_getCertificateAt,
27     AVmAttestationResult_getCertificateCount, AVmAttestationResult_getPrivateKey,
28     AVmAttestationResult_sign, AVmAttestationStatus, AVmAttestationStatus_toString,
29     AVmPayload_requestAttestation,
30 };
31 
32 /// Entry point of the Service VM client.
33 #[allow(non_snake_case)]
34 #[no_mangle]
AVmPayload_main()35 pub extern "C" fn AVmPayload_main() {
36     android_logger::init_once(
37         android_logger::Config::default()
38             .with_tag("service_vm_client")
39             .with_max_level(log::LevelFilter::Debug),
40     );
41     // Redirect panic messages to logcat.
42     panic::set_hook(Box::new(|panic_info| {
43         error!("{}", panic_info);
44     }));
45     if let Err(e) = try_main() {
46         error!("failed with {:?}", e);
47         std::process::exit(1);
48     }
49 }
50 
try_main() -> Result<()>51 fn try_main() -> Result<()> {
52     info!("Welcome to Service VM Client!");
53 
54     let too_big_challenge = &[0u8; 66];
55     let res = AttestationResult::request_attestation(too_big_challenge);
56     ensure!(res.is_err());
57     let status = res.unwrap_err();
58     ensure!(
59         status == AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE,
60         "Unexpected status: {:?}",
61         status
62     );
63     info!("Status: {:?}", status_to_cstr(status));
64 
65     // The data below is only a placeholder generated randomly with urandom
66     let challenge = &[
67         0x6c, 0xad, 0x52, 0x50, 0x15, 0xe7, 0xf4, 0x1d, 0xa5, 0x60, 0x7e, 0xd2, 0x7d, 0xf1, 0x51,
68         0x67, 0xc3, 0x3e, 0x73, 0x9b, 0x30, 0xbd, 0x04, 0x20, 0x2e, 0xde, 0x3b, 0x1d, 0xc8, 0x07,
69         0x11, 0x7b,
70     ];
71     let res = AttestationResult::request_attestation(challenge)
72         .map_err(|e| anyhow!("Unexpected status: {:?}", status_to_cstr(e)))?;
73 
74     let cert_chain = res.certificate_chain()?;
75     info!("Attestation result certificateChain = {:?}", cert_chain);
76 
77     let private_key = res.private_key()?;
78     info!("Attestation result privateKey = {:?}", private_key);
79 
80     let message = b"Hello from Service VM client";
81     info!("Signing message: {:?}", message);
82     let signature = res.sign(message)?;
83     info!("Signature: {:?}", signature);
84 
85     Ok(())
86 }
87 
88 #[derive(Debug)]
89 struct AttestationResult(NonNull<AVmAttestationResult>);
90 
91 impl AttestationResult {
request_attestation(challenge: &[u8]) -> result::Result<Self, AVmAttestationStatus>92     fn request_attestation(challenge: &[u8]) -> result::Result<Self, AVmAttestationStatus> {
93         let mut res: *mut AVmAttestationResult = ptr::null_mut();
94         // SAFETY: It is safe as we only read the challenge within its bounds and the
95         // function does not retain any reference to it.
96         let status = unsafe {
97             AVmPayload_requestAttestation(
98                 challenge.as_ptr() as *const c_void,
99                 challenge.len(),
100                 &mut res,
101             )
102         };
103         if status == AVmAttestationStatus::ATTESTATION_OK {
104             info!("Attestation succeeds. Status: {:?}", status_to_cstr(status));
105             let res = NonNull::new(res).expect("The attestation result is null");
106             Ok(Self(res))
107         } else {
108             Err(status)
109         }
110     }
111 
certificate_chain(&self) -> Result<Vec<Box<[u8]>>>112     fn certificate_chain(&self) -> Result<Vec<Box<[u8]>>> {
113         let num_certs = get_certificate_count(self.as_ref());
114         let mut certs = Vec::with_capacity(num_certs);
115         for i in 0..num_certs {
116             certs.push(get_certificate_at(self.as_ref(), i)?);
117         }
118         Ok(certs)
119     }
120 
private_key(&self) -> Result<Box<[u8]>>121     fn private_key(&self) -> Result<Box<[u8]>> {
122         get_private_key(self.as_ref())
123     }
124 
sign(&self, message: &[u8]) -> Result<Box<[u8]>>125     fn sign(&self, message: &[u8]) -> Result<Box<[u8]>> {
126         sign_with_attested_key(self.as_ref(), message)
127     }
128 }
129 
130 impl AsRef<AVmAttestationResult> for AttestationResult {
as_ref(&self) -> &AVmAttestationResult131     fn as_ref(&self) -> &AVmAttestationResult {
132         // SAFETY: This field is private, and only populated with a successful call to
133         // `AVmPayload_requestAttestation`.
134         unsafe { self.0.as_ref() }
135     }
136 }
137 
138 impl Drop for AttestationResult {
drop(&mut self)139     fn drop(&mut self) {
140         // SAFETY: This field is private, and only populated with a successful call to
141         // `AVmPayload_requestAttestation`, and not freed elsewhere.
142         unsafe { AVmAttestationResult_free(self.0.as_ptr()) };
143     }
144 }
145 
get_certificate_count(res: &AVmAttestationResult) -> usize146 fn get_certificate_count(res: &AVmAttestationResult) -> usize {
147     // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
148     // before getting freed.
149     unsafe { AVmAttestationResult_getCertificateCount(res) }
150 }
151 
get_certificate_at(res: &AVmAttestationResult, index: usize) -> Result<Box<[u8]>>152 fn get_certificate_at(res: &AVmAttestationResult, index: usize) -> Result<Box<[u8]>> {
153     let size =
154         // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
155         // before getting freed.
156         unsafe { AVmAttestationResult_getCertificateAt(res, index, ptr::null_mut(), 0) };
157     let mut cert = vec![0u8; size];
158     // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
159     // before getting freed. This function only writes within the bounds of `cert`.
160     // And `cert` cannot overlap `res` because we just allocated it.
161     let size = unsafe {
162         AVmAttestationResult_getCertificateAt(
163             res,
164             index,
165             cert.as_mut_ptr() as *mut c_void,
166             cert.len(),
167         )
168     };
169     ensure!(size == cert.len());
170     Ok(cert.into_boxed_slice())
171 }
172 
get_private_key(res: &AVmAttestationResult) -> Result<Box<[u8]>>173 fn get_private_key(res: &AVmAttestationResult) -> Result<Box<[u8]>> {
174     let size =
175         // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
176         // before getting freed.
177         unsafe { AVmAttestationResult_getPrivateKey(res, ptr::null_mut(), 0) };
178     let mut private_key = vec![0u8; size];
179     // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
180     // before getting freed. This function only writes within the bounds of `private_key`.
181     // And `private_key` cannot overlap `res` because we just allocated it.
182     let size = unsafe {
183         AVmAttestationResult_getPrivateKey(
184             res,
185             private_key.as_mut_ptr() as *mut c_void,
186             private_key.len(),
187         )
188     };
189     ensure!(size == private_key.len());
190     Ok(private_key.into_boxed_slice())
191 }
192 
sign_with_attested_key(res: &AVmAttestationResult, message: &[u8]) -> Result<Box<[u8]>>193 fn sign_with_attested_key(res: &AVmAttestationResult, message: &[u8]) -> Result<Box<[u8]>> {
194     // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
195     // before getting freed.
196     let size = unsafe {
197         AVmAttestationResult_sign(
198             res,
199             message.as_ptr() as *const c_void,
200             message.len(),
201             ptr::null_mut(),
202             0,
203         )
204     };
205     let mut signature = vec![0u8; size];
206     // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
207     // before getting freed. This function only writes within the bounds of `signature`.
208     // And `signature` cannot overlap `res` because we just allocated it.
209     let size = unsafe {
210         AVmAttestationResult_sign(
211             res,
212             message.as_ptr() as *const c_void,
213             message.len(),
214             signature.as_mut_ptr() as *mut c_void,
215             signature.len(),
216         )
217     };
218     ensure!(size == signature.len());
219     Ok(signature.into_boxed_slice())
220 }
221 
status_to_cstr(status: AVmAttestationStatus) -> &'static CStr222 fn status_to_cstr(status: AVmAttestationStatus) -> &'static CStr {
223     // SAFETY: The function only reads the given enum status and returns a pointer to a
224     // static string.
225     let message = unsafe { AVmAttestationStatus_toString(status) };
226     // SAFETY: The pointer returned by `AVmAttestationStatus_toString` is guaranteed to
227     // point to a valid C String that lives forever.
228     unsafe { CStr::from_ptr(message) }
229 }
230