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