1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 use std::error::Error;
18 use std::ffi::{c_void, CStr};
19 use std::fmt::{self, Display};
20 use std::iter::FusedIterator;
21 use std::ptr::{self, NonNull};
22 
23 use vm_payload_bindgen::{
24     AVmAttestationResult, AVmAttestationResult_free, AVmAttestationResult_getCertificateAt,
25     AVmAttestationResult_getCertificateCount, AVmAttestationResult_getPrivateKey,
26     AVmAttestationResult_sign, AVmAttestationStatus, AVmAttestationStatus_toString,
27     AVmPayload_requestAttestation, AVmPayload_requestAttestationForTesting,
28 };
29 
30 /// Holds the result of a successful Virtual Machine attestation request.
31 /// See [`request_attestation`].
32 #[derive(Debug)]
33 pub struct AttestationResult {
34     result: NonNull<AVmAttestationResult>,
35 }
36 
37 /// Error type that can be returned from an unsuccessful Virtual Machine attestation request.
38 /// See [`request_attestation`].
39 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
40 pub enum AttestationError {
41     /// The challenge size was not between 0 and 64 bytes (inclusive).
42     InvalidChallenge,
43     /// The attempt to attest the VM failed. A subsequent request may succeed.
44     AttestationFailed,
45     /// VM attestation is not supported in the current environment.
46     AttestationUnsupported,
47 }
48 
49 impl Error for AttestationError {}
50 
51 impl Display for AttestationError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>52     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
53         let status = match self {
54             Self::InvalidChallenge => AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE,
55             Self::AttestationFailed => AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED,
56             Self::AttestationUnsupported => AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED,
57         };
58         // SAFETY: AVmAttestationStatus_toString always returns a non-null pointer to a
59         // nul-terminated C string with static lifetime (which is valid UTF-8).
60         let c_str = unsafe { CStr::from_ptr(AVmAttestationStatus_toString(status)) };
61         let str = c_str.to_str().expect("Invalid UTF-8 for AVmAttestationStatus");
62         f.write_str(str)
63     }
64 }
65 
66 impl Drop for AttestationResult {
drop(&mut self)67     fn drop(&mut self) {
68         let ptr = self.result.as_ptr();
69 
70         // SAFETY: The `result` field is private, and only populated with a successful call to
71         // `AVmPayload_requestAttestation`, and not freed elsewhere.
72         unsafe { AVmAttestationResult_free(ptr) };
73     }
74 }
75 
76 // SAFETY: The API functions that accept the `AVmAttestationResult` pointer are all safe to call
77 // from any thread, including `AVmAttestationResult_free` which is called only on drop.
78 unsafe impl Send for AttestationResult {}
79 
80 // SAFETY: There is no interior mutation here; any future functions that might mutate the data would
81 // require a non-const pointer and hence need `&mut self` here. The only existing such function is
82 // `AVmAttestationResult_free` where we take a mutable reference guaranteeing no other references
83 // exist. The raw API functions are safe to call from any thread.
84 unsafe impl Sync for AttestationResult {}
85 
86 /// Requests the remote attestation of this VM.
87 ///
88 /// On success the supplied [`challenge`] will be included in the certificate chain accessible from
89 /// the [`AttestationResult`]; this can be used as proof of the freshness of the attestation.
90 ///
91 /// The challenge should be no more than 64 bytes long or the request will fail.
request_attestation(challenge: &[u8]) -> Result<AttestationResult, AttestationError>92 pub fn request_attestation(challenge: &[u8]) -> Result<AttestationResult, AttestationError> {
93     let mut result: *mut AVmAttestationResult = ptr::null_mut();
94     // SAFETY: We only read the challenge within its bounds and the function does not retain any
95     // reference to it.
96     let status = unsafe {
97         AVmPayload_requestAttestation(
98             challenge.as_ptr() as *const c_void,
99             challenge.len(),
100             &mut result,
101         )
102     };
103     AttestationResult::new(status, result)
104 }
105 
106 /// A variant of [`request_attestation`] used for testing purposes. This should not be used by
107 /// normal VMs, and is not available to app owned VMs.
request_attestation_for_testing( challenge: &[u8], ) -> Result<AttestationResult, AttestationError>108 pub fn request_attestation_for_testing(
109     challenge: &[u8],
110 ) -> Result<AttestationResult, AttestationError> {
111     let mut result: *mut AVmAttestationResult = ptr::null_mut();
112     // SAFETY: We only read the challenge within its bounds and the function does not retain any
113     // reference to it.
114     let status = unsafe {
115         AVmPayload_requestAttestationForTesting(
116             challenge.as_ptr() as *const c_void,
117             challenge.len(),
118             &mut result,
119         )
120     };
121     AttestationResult::new(status, result)
122 }
123 
124 impl AttestationResult {
new( status: AVmAttestationStatus, result: *mut AVmAttestationResult, ) -> Result<AttestationResult, AttestationError>125     fn new(
126         status: AVmAttestationStatus,
127         result: *mut AVmAttestationResult,
128     ) -> Result<AttestationResult, AttestationError> {
129         match status {
130             AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE => {
131                 Err(AttestationError::InvalidChallenge)
132             }
133             AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED => {
134                 Err(AttestationError::AttestationFailed)
135             }
136             AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => {
137                 Err(AttestationError::AttestationUnsupported)
138             }
139             AVmAttestationStatus::ATTESTATION_OK => {
140                 let result = NonNull::new(result)
141                     .expect("Attestation succeeded but the attestation result is null");
142                 Ok(AttestationResult { result })
143             }
144         }
145     }
146 
as_const_ptr(&self) -> *const AVmAttestationResult147     fn as_const_ptr(&self) -> *const AVmAttestationResult {
148         self.result.as_ptr().cast_const()
149     }
150 
151     /// Returns the attested private key. This is the ECDSA P-256 private key corresponding to the
152     /// public key described by the leaf certificate in the attested
153     /// [certificate chain](AttestationResult::certificate_chain). It is a DER-encoded
154     /// `ECPrivateKey` structure as specified in
155     /// [RFC 5915 s3](https://datatracker.ietf.org/doc/html/rfc5915#section-3).
156     ///
157     /// Note: The [`sign_message`](AttestationResult::sign_message) method allows signing with the
158     /// key without retrieving it.
private_key(&self) -> Vec<u8>159     pub fn private_key(&self) -> Vec<u8> {
160         let ptr = self.as_const_ptr();
161 
162         let size =
163             // SAFETY: We own the `AVmAttestationResult` pointer, so it is valid. The function
164             // writes no data since we pass a zero size, and null is explicitly allowed for the
165             // destination in that case.
166             unsafe { AVmAttestationResult_getPrivateKey(ptr, ptr::null_mut(), 0) };
167 
168         let mut private_key = vec![0u8; size];
169         // SAFETY: We own the `AVmAttestationResult` pointer, so it is valid. The function only
170         // writes within the bounds of `private_key`, which we just allocated so cannot be aliased.
171         let size = unsafe {
172             AVmAttestationResult_getPrivateKey(
173                 ptr,
174                 private_key.as_mut_ptr() as *mut c_void,
175                 private_key.len(),
176             )
177         };
178         assert_eq!(size, private_key.len());
179         private_key
180     }
181 
182     /// Signs the given message using the attested private key. The signature uses ECDSA P-256; the
183     /// message is first hashed with SHA-256 and then it is signed with the attested EC P-256
184     /// [private key](AttestationResult::private_key).
185     ///
186     /// The signature is a DER-encoded `ECDSASignature`` structure as described in
187     /// [RFC 6979](https://datatracker.ietf.org/doc/html/rfc6979).
sign_message(&self, message: &[u8]) -> Vec<u8>188     pub fn sign_message(&self, message: &[u8]) -> Vec<u8> {
189         let ptr = self.as_const_ptr();
190 
191         // SAFETY: We own the `AVmAttestationResult` pointer, so it is valid. The function
192         // writes no data since we pass a zero size, and null is explicitly allowed for the
193         // destination in that case.
194         let size = unsafe {
195             AVmAttestationResult_sign(
196                 ptr,
197                 message.as_ptr() as *const c_void,
198                 message.len(),
199                 ptr::null_mut(),
200                 0,
201             )
202         };
203 
204         let mut signature = vec![0u8; size];
205         // SAFETY: We own the `AVmAttestationResult` pointer, so it is valid. The function only
206         // writes within the bounds of `signature`, which we just allocated so cannot be aliased.
207         let size = unsafe {
208             AVmAttestationResult_sign(
209                 ptr,
210                 message.as_ptr() as *const c_void,
211                 message.len(),
212                 signature.as_mut_ptr() as *mut c_void,
213                 signature.len(),
214             )
215         };
216         assert!(size <= signature.len());
217         signature.truncate(size);
218         signature
219     }
220 
221     /// Returns an iterator over the certificates forming the certificate chain for the VM, and its
222     /// public key, obtained by the attestation process.
223     ///
224     /// The certificate chain consists of a sequence of DER-encoded X.509 certificates that form
225     /// the attestation key's certificate chain. It starts with the leaf certificate covering the
226     /// attested public key and ends with the root certificate.
certificate_chain(&self) -> CertIterator227     pub fn certificate_chain(&self) -> CertIterator {
228         // SAFETY: We own the `AVmAttestationResult` pointer, so it is valid.
229         let count = unsafe { AVmAttestationResult_getCertificateCount(self.as_const_ptr()) };
230 
231         CertIterator { result: self, count, current: 0 }
232     }
233 
certificate(&self, index: usize) -> Vec<u8>234     fn certificate(&self, index: usize) -> Vec<u8> {
235         let ptr = self.as_const_ptr();
236 
237         let size =
238             // SAFETY: We own the `AVmAttestationResult` pointer, so it is valid. The function
239             // writes no data since we pass a zero size, and null is explicitly allowed for the
240             // destination in that case. The function will panic if `index` is out of range (which
241             // is safe).
242             unsafe { AVmAttestationResult_getCertificateAt(ptr, index, ptr::null_mut(), 0) };
243 
244         let mut cert = vec![0u8; size];
245         // SAFETY: We own the `AVmAttestationResult` pointer, so it is valid. The function only
246         // writes within the bounds of `cert`, which we just allocated so cannot be aliased.
247         let size = unsafe {
248             AVmAttestationResult_getCertificateAt(
249                 ptr,
250                 index,
251                 cert.as_mut_ptr() as *mut c_void,
252                 cert.len(),
253             )
254         };
255         assert_eq!(size, cert.len());
256         cert
257     }
258 }
259 
260 /// An iterator over the DER-encoded X.509 certificates containin in an [`AttestationResult`].
261 /// See [`certificate_chain`](AttestationResult::certificate_chain) for more details.
262 pub struct CertIterator<'a> {
263     result: &'a AttestationResult,
264     count: usize,
265     current: usize, // Invariant: current <= count
266 }
267 
268 impl<'a> Iterator for CertIterator<'a> {
269     type Item = Vec<u8>;
270 
next(&mut self) -> Option<Self::Item>271     fn next(&mut self) -> Option<Self::Item> {
272         if self.current < self.count {
273             let cert = self.result.certificate(self.current);
274             self.current += 1;
275             Some(cert)
276         } else {
277             None
278         }
279     }
280 
size_hint(&self) -> (usize, Option<usize>)281     fn size_hint(&self) -> (usize, Option<usize>) {
282         let size = self.count - self.current;
283         (size, Some(size))
284     }
285 }
286 
287 impl<'a> ExactSizeIterator for CertIterator<'a> {}
288 impl<'a> FusedIterator for CertIterator<'a> {}
289