rust_sgx_util/
ias.rs

1use crate::{c, Error, Nonce, Quote, Result};
2#[cfg(feature = "with_serde")]
3use serde::{Deserialize, Serialize};
4use std::ffi::CString;
5use std::ops::Deref;
6use std::path::Path;
7use std::ptr::{self, NonNull};
8use std::str::FromStr;
9use std::{fmt, slice, u32};
10
11const IAS_VERIFY_URL: &str = "https://api.trustedservices.intel.com/sgx/dev/attestation/v3/report";
12const IAS_SIGRL_URL: &str = "https://api.trustedservices.intel.com/sgx/dev/attestation/v3/sigrl";
13
14/// Represents a handle to Intel's Attestation Service. It allows the user
15/// to perform operations such as getting a SigRL for a specified [`GroupId`],
16/// or verifying a specified quote with the IAS.
17///
18/// [`GroupId`]: struct.GroupId.html
19pub struct IasHandle {
20    // We need to store `verify_url` and `sigrl_url` due to a bug in the current
21    // implementation of `sgx_util` lib which does not copy out the buffers
22    // passed in as args to `ias_init` function.
23    #[allow(dead_code)]
24    verify_url: CString,
25    #[allow(dead_code)]
26    sigrl_url: CString,
27    context: NonNull<c::IasContext>,
28}
29
30impl IasHandle {
31    // TODO API key should probably have its own type that does
32    // at the very least some length validation
33    /// Create new instance with the specified `api_key` API key,
34    /// IAS verification URL `verify_url`, and IAS SigRL URL `sigrl_url`.
35    ///
36    /// By default, the following URLs are used:
37    /// * IAS verification - [dev/attestation/v3/report]
38    /// * IAS SigRL - [dev/attestation/v3/sigrl]
39    ///
40    /// [dev/attestation/v3/report]: https://api.trustedservices.intel.com/sgx/dev/attestation/v3/report
41    /// [dev/attestation/v3/sigrl]: https://api.trustedservices.intel.com/sgx/dev/attestation/v3/sigrl
42    ///
43    /// # Errors
44    ///
45    /// This function will fail with [`Error::IasInitNullPtr`] if initialisation
46    /// of the handle is unsuccessful, or if converting input arguments to
47    /// `CString` fails.
48    ///
49    /// [`Error::IasInitNullPtr`]: enum.Error.html#variant.IasInitNullPtr
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// # use rust_sgx_util::*;
55    /// # fn main() -> anyhow::Result<()> {
56    /// let _handle = IasHandle::new("012345abcdef", None, None)?;
57    /// # Ok(())
58    /// # }
59    /// ```
60    pub fn new(api_key: &str, verify_url: Option<&str>, sigrl_url: Option<&str>) -> Result<Self> {
61        let api_key = CString::new(api_key)?;
62        let verify_url = verify_url.unwrap_or(IAS_VERIFY_URL);
63        let verify_url = CString::new(verify_url)?;
64        let sigrl_url = sigrl_url.unwrap_or(IAS_SIGRL_URL);
65        let sigrl_url = CString::new(sigrl_url)?;
66        let raw_context =
67            unsafe { c::ias_init(api_key.as_ptr(), verify_url.as_ptr(), sigrl_url.as_ptr()) };
68        let context = NonNull::new(raw_context).ok_or(Error::IasInitNullPtr)?;
69        Ok(Self {
70            verify_url,
71            sigrl_url,
72            context,
73        })
74    }
75
76    /// Obtain SigRL for the given `group_id`.
77    ///
78    /// # Errors
79    ///
80    /// This function will fail with [`Error::IasGetSigrlNonZero(_)`] if the
81    /// `group_id` is invalid, or the `IasHandle` was created with an
82    /// invalid IAS verification URL.
83    ///
84    /// [`Error::IasGetSigrlNonZero(_)`]: enum.Error.html#variant.IasGetSigrlNonZero
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// # use rust_sgx_util::*;
90    /// use std::str::FromStr;
91    /// # fn main() -> anyhow::Result<()> {
92    /// let handle = IasHandle::new("012345abcdef", None, None)?;
93    /// let group_id = GroupId::from_str("01234567")?;
94    /// let res = handle.get_sigrl(&group_id);
95    /// assert!(res.is_err());
96    /// # Ok(())
97    /// # }
98    /// ```
99    pub fn get_sigrl(&self, group_id: &GroupId) -> Result<Option<Sigrl>> {
100        let mut size: usize = 0;
101        let mut raw = ptr::null_mut();
102        let ret = unsafe {
103            c::ias_get_sigrl(
104                self.context.as_ptr(),
105                group_id.as_ptr(),
106                &mut size,
107                &mut raw,
108            )
109        };
110        if ret == 0 {
111            if size == 0 {
112                // No SigRL for given EPID group id
113                Ok(None)
114            } else {
115                let sigrl = unsafe { Sigrl::new(raw as *const u8, size) };
116                Ok(Some(sigrl))
117            }
118        } else {
119            Err(Error::IasGetSigrlNonZero(ret))
120        }
121    }
122
123    /// Verify provided quote.
124    ///
125    /// # Errors
126    ///
127    /// This function will fail with [`Error::IasVerifyQuoteNonZero(_)`] if the
128    /// provided `quote` is invalid, or the `nonce`, or if the IAS server
129    /// returns a non 200 status code.
130    ///
131    /// [`Error::IasVerifyQuoteNonZero(_)`]: enum.Error.html#variant.IasVerifyQuoteNonZero
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// # use rust_sgx_util::*;
137    /// # fn main() -> anyhow::Result<()> {
138    /// let handle = IasHandle::new("012345abcdef", None, None)?;
139    /// let quote = Quote::from(vec![0u8; 100]);
140    /// let res = handle.verify_quote(&quote, None, None, None, None, None);
141    /// assert!(res.is_err());
142    /// # Ok(())
143    /// # }
144    /// ```
145    pub fn verify_quote(
146        &self,
147        quote: &Quote,
148        nonce: Option<&Nonce>,
149        report_path: Option<&Path>,
150        sig_path: Option<&Path>,
151        cert_path: Option<&Path>,
152        advisory_path: Option<&Path>,
153    ) -> Result<()> {
154        let nonce = nonce.map(|nonce| CString::new(nonce.deref())).transpose()?;
155        let report_path = report_path.map(|path| path_to_c_string(path)).transpose()?;
156        let sig_path = sig_path.map(|path| path_to_c_string(path)).transpose()?;
157        let cert_path = cert_path.map(|path| path_to_c_string(path)).transpose()?;
158        let advisory_path = advisory_path
159            .map(|path| path_to_c_string(path))
160            .transpose()?;
161        let ret = unsafe {
162            c::ias_verify_quote(
163                self.context.as_ptr(),
164                quote.as_ptr() as *const _,
165                quote.len(),
166                match &nonce {
167                    Some(nonce) => nonce.as_ptr(),
168                    None => ptr::null(),
169                },
170                match &report_path {
171                    Some(path) => path.as_ptr(),
172                    None => ptr::null(),
173                },
174                match &sig_path {
175                    Some(path) => path.as_ptr(),
176                    None => ptr::null(),
177                },
178                match &cert_path {
179                    Some(path) => path.as_ptr(),
180                    None => ptr::null(),
181                },
182                match &advisory_path {
183                    Some(path) => path.as_ptr(),
184                    None => ptr::null(),
185                }
186            )
187        };
188        if ret == 0 {
189            Ok(())
190        } else {
191            Err(Error::IasVerifyQuoteNonZero(ret))
192        }
193    }
194}
195
196impl Drop for IasHandle {
197    fn drop(&mut self) {
198        unsafe { c::ias_cleanup(self.context.as_ptr()) }
199    }
200}
201
202#[cfg(unix)]
203fn path_to_c_string(path: &Path) -> Result<CString> {
204    use std::os::unix::ffi::OsStrExt;
205    let s = CString::new(path.as_os_str().as_bytes())?;
206    Ok(s)
207}
208
209#[cfg(windows)]
210fn path_to_c_string(path: &Path) -> Result<CString> {
211    use std::os::windows::ffi::OsStringExt;
212    let utf16: Vec<_> = path.as_os_str().encode_wide().collect();
213    let s = String::from_utf16(utf16)?;
214    let s = CString::new(s.as_bytes())?;
215    Ok(s)
216}
217
218/// A thin wrapper around vector of bytes. Stores the result of
219/// [`IasHandle::get_sigrl`] function call, i.e., the SigRL
220/// for the specified [`GroupId`].
221///
222/// [`IasHandle::get_sigrl`]: struct.IasHandle.html#method.get_sigrl
223/// [`GroupId`]: struct.GroupId.html
224///
225/// # Accessing the underlying bytes buffer
226///
227/// `Sigrl` implements `Deref<Target=[u8]>`, therefore dereferencing it will
228/// yield its inner buffer of bytes.
229///
230/// # Serializing/deserializing
231///
232/// With `with_serde` feature enabled, `Sigrl` can be serialized and deserialized
233/// as base64 `String`.
234#[derive(Debug, Clone)]
235#[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))]
236pub struct Sigrl(#[cfg_attr(feature = "with_serde", serde(with = "crate::ser_de"))] Vec<u8>);
237
238impl Sigrl {
239    unsafe fn new(raw: *const u8, size: usize) -> Self {
240        let slice = slice::from_raw_parts(raw, size);
241        Self(slice.to_vec())
242    }
243}
244
245impl Deref for Sigrl {
246    type Target = [u8];
247
248    fn deref(&self) -> &Self::Target {
249        &self.0
250    }
251}
252
253impl fmt::Display for Sigrl {
254    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255        f.write_str("Sigrl(")?;
256        for b in &self.0 {
257            f.write_fmt(format_args!("{:#b}", b))?;
258        }
259        f.write_str(")")
260    }
261}
262
263/// Represents EPID group ID.
264///
265/// This structure is necessary to invoke [`IasHandle::get_sigrl`] function.
266///
267/// [`IasHandle::get_sigrl`]: struct.IasHandle.html#method.get_sigrl
268///
269/// # Creating `GroupId`
270///
271/// Currently, the only way to create an instance of `GroupId`, is from `&str`
272/// slice via the `std::str::FromStr::from_str` method. Note also that currently
273/// prepending "0x" to the string is invalid, and will result in `Error::ParseInt(_)`
274/// error.
275///
276/// ```
277/// # use rust_sgx_util::GroupId;
278/// use std::str::FromStr;
279/// assert!(GroupId::from_str("01234567").is_ok());
280/// assert!(GroupId::from_str("0x01234567").is_err()); // prepending "0x" is currently invalid
281/// ```
282///
283/// # Accessing the underlying bytes buffer
284///
285/// `GroupId` implements `Deref<Target=[u8]>`, therefore dereferencing it will
286/// yield its inner buffer of bytes.
287#[derive(Debug, Clone)]
288#[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))]
289// TODO cleanup construction and ser/de
290pub struct GroupId([u8; 4]);
291
292impl Deref for GroupId {
293    type Target = [u8];
294
295    fn deref(&self) -> &Self::Target {
296        &self.0
297    }
298}
299
300impl FromStr for GroupId {
301    type Err = Error;
302
303    fn from_str(s: &str) -> Result<Self> {
304        let parsed = u32::from_str_radix(s, 16)?;
305        Ok(GroupId(parsed.to_le_bytes()))
306    }
307}
308
309impl fmt::Display for GroupId {
310    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
311        write!(f, "GroupId({:#010x})", u32::from_le_bytes(self.0))
312    }
313}