22// License, v. 2.0. If a copy of the MPL was not distributed with this
33// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44
5- // Copyright 2023 Oxide Computer Company
5+ // Copyright 2026 Oxide Computer Company
66
77//! An interface to libipcc (inter-processor communications channel) which
8- //! currently supports looking up values stored in the SP by key. These
9- //! values are variously static, passed from the control plane to the SP
10- //! (through MGS) or set from userland via libipcc.
8+ //! currently supports looking up values stored in the SP by key as well as RoT
9+ //! attestation operations.
10+ //! The looked up SP values are variously static, passed from the control plane
11+ //! to the SP (through MGS) or set from userland via libipcc.
1112
12- use libipcc:: { IpccError , IpccHandle } ;
13+ use attest_data:: messages:: { HostToRotCommand , MAX_REQUEST_SIZE , RotToHost } ;
14+ use attest_data:: { Attestation , Log , Nonce } ;
15+ use libipcc:: IPCC_MAX_DATA_SIZE ;
1316use omicron_uuid_kinds:: MupdateUuid ;
1417use serde:: Deserialize ;
1518use serde:: Serialize ;
1619use thiserror:: Error ;
1720use tufaceous_artifact:: ArtifactHash ;
1821
22+ pub use libipcc:: IpccError ;
23+
1924#[ cfg( test) ]
2025use proptest:: arbitrary:: any;
2126#[ cfg( test) ]
2227use proptest:: strategy:: Strategy ;
28+ use x509_cert:: PkiPath ;
29+ use x509_cert:: der:: { self , Decode , Reader } ;
2330
2431/// Supported keys.
2532///
@@ -134,6 +141,18 @@ pub enum InstallinatorImageIdError {
134141 DeserializationFailed ( String ) ,
135142}
136143
144+ #[ derive( Debug , Error ) ]
145+ pub enum AttestError {
146+ #[ error( transparent) ]
147+ Ipcc ( #[ from] IpccError ) ,
148+ #[ error( "failed to send ipcc message to RoT: {0}" ) ]
149+ HostToRot ( #[ from] attest_data:: messages:: HostToRotError ) ,
150+ #[ error( "deserializing {0:?} failed: {1}" ) ]
151+ Deserialize ( RotToHost , String ) ,
152+ #[ error( transparent) ]
153+ Certificate ( #[ from] x509_cert:: der:: Error ) ,
154+ }
155+
137156/// These are the IPCC keys we can look up.
138157/// NB: These keys match the definitions found in libipcc (RFD 316) and should
139158/// match the values in `[ipcc::Key]` one-to-one.
@@ -151,13 +170,13 @@ enum IpccKey {
151170/// Interface to the inter-processor communications channel.
152171/// For more information see rfd 316.
153172pub struct Ipcc {
154- handle : IpccHandle ,
173+ handle : libipcc :: IpccHandle ,
155174}
156175
157176impl Ipcc {
158177 /// Creates a new `Ipcc` instance.
159178 pub fn new ( ) -> Result < Self , IpccError > {
160- let handle = IpccHandle :: new ( ) ?;
179+ let handle = libipcc :: IpccHandle :: new ( ) ?;
161180 Ok ( Self { handle } )
162181 }
163182
@@ -173,6 +192,112 @@ impl Ipcc {
173192 . map_err ( InstallinatorImageIdError :: DeserializationFailed ) ?;
174193 Ok ( id)
175194 }
195+
196+ pub fn get_measurement_log ( & self ) -> Result < Log , AttestError > {
197+ let mut rot_message = vec ! [ 0 ; MAX_REQUEST_SIZE ] ;
198+ let mut rot_resp = vec ! [ 0 ; IPCC_MAX_DATA_SIZE ] ;
199+ // Serializing is infallible
200+ let rot_req = match attest_data:: messages:: serialize (
201+ & mut rot_message,
202+ & HostToRotCommand :: GetMeasurementLog ,
203+ |_| 0 ,
204+ ) {
205+ Ok ( len) => & rot_message[ ..len] ,
206+ Err ( err) => unreachable ! (
207+ "failed to serialize GetMeasurementLog command: {err}"
208+ ) ,
209+ } ;
210+ let resp_len = self . handle . rot_request ( rot_req, & mut rot_resp) ?;
211+ let data = attest_data:: messages:: parse_response (
212+ & rot_resp[ ..resp_len] ,
213+ RotToHost :: RotMeasurementLog ,
214+ ) ?;
215+ let log = match hubpack:: deserialize ( data) {
216+ Ok ( ( log, _) ) => log,
217+ Err ( err) => {
218+ return Err ( AttestError :: Deserialize (
219+ RotToHost :: RotMeasurementLog ,
220+ format ! ( "{err}" ) ,
221+ ) ) ;
222+ }
223+ } ;
224+ Ok ( log)
225+ }
226+
227+ pub fn get_certificates ( & self ) -> Result < PkiPath , AttestError > {
228+ let mut rot_message = vec ! [ 0 ; MAX_REQUEST_SIZE ] ;
229+ let mut rot_resp = vec ! [ 0 ; IPCC_MAX_DATA_SIZE ] ;
230+ // Serializing is infallible
231+ let rot_req = match attest_data:: messages:: serialize (
232+ & mut rot_message,
233+ & HostToRotCommand :: GetCertificates ,
234+ |_| 0 ,
235+ ) {
236+ Ok ( len) => & rot_message[ ..len] ,
237+ Err ( err) => unreachable ! (
238+ "failed to serialize GetCertificates command: {err}"
239+ ) ,
240+ } ;
241+ let resp_len = self . handle . rot_request ( rot_req, & mut rot_resp) ?;
242+ let data = attest_data:: messages:: parse_response (
243+ & rot_resp[ ..resp_len] ,
244+ RotToHost :: RotCertificates ,
245+ ) ?;
246+
247+ // The returned payload is the DER encoded certificate chain itself
248+ // which we decode into a more usable `PkiPath`
249+ let mut certs = PkiPath :: new ( ) ;
250+ assert ! ( data. len( ) < u32 :: from( der:: Length :: MAX ) as usize ) ;
251+ let mut reader = der:: SliceReader :: new ( data) . unwrap ( ) ;
252+ while !reader. is_finished ( ) {
253+ let cert = reader
254+ . tlv_bytes ( )
255+ . and_then ( |bytes| x509_cert:: Certificate :: from_der ( bytes) )
256+ . map_err ( |err| {
257+ AttestError :: Deserialize (
258+ RotToHost :: RotCertificates ,
259+ format ! ( "[{}] {err}" , certs. len( ) ) ,
260+ )
261+ } ) ?;
262+ certs. push ( cert) ;
263+ }
264+
265+ Ok ( certs)
266+ }
267+
268+ pub fn attest ( & self , nonce : Nonce ) -> Result < Attestation , AttestError > {
269+ let mut rot_message = vec ! [ 0 ; MAX_REQUEST_SIZE ] ;
270+ let mut rot_resp = vec ! [ 0 ; IPCC_MAX_DATA_SIZE ] ;
271+ // Serializing is infallible
272+ let rot_req = match attest_data:: messages:: serialize (
273+ & mut rot_message,
274+ & HostToRotCommand :: Attest ,
275+ |buf| {
276+ buf[ ..Nonce :: LENGTH ] . copy_from_slice ( nonce. as_ref ( ) ) ;
277+ Nonce :: LENGTH
278+ } ,
279+ ) {
280+ Ok ( len) => & rot_message[ ..len] ,
281+ Err ( err) => {
282+ unreachable ! ( "failed to serialize Attest command: {err}" )
283+ }
284+ } ;
285+ let resp_len = self . handle . rot_request ( rot_req, & mut rot_resp) ?;
286+ let data = attest_data:: messages:: parse_response (
287+ & rot_resp[ ..resp_len] ,
288+ RotToHost :: RotAttestation ,
289+ ) ?;
290+ let attestation = match hubpack:: deserialize ( data) {
291+ Ok ( ( attestation, _) ) => attestation,
292+ Err ( err) => {
293+ return Err ( AttestError :: Deserialize (
294+ RotToHost :: RotAttestation ,
295+ format ! ( "{err}" ) ,
296+ ) ) ;
297+ }
298+ } ;
299+ Ok ( attestation)
300+ }
176301}
177302
178303#[ cfg( test) ]
0 commit comments