@@ -16,11 +16,13 @@ use crate::{
1616 codec:: { Codec , MessageBuf } ,
1717 commands:: measurements:: {
1818 GetMeasurementsReqAttr , GetMeasurementsReqCommon , GetMeasurementsReqSignature ,
19+ MeasurementBlockHeader , MeasurementOperation , MeasurementsRspFixed ,
1920 } ,
2021 context:: SpdmContext ,
2122 error:: { CommandError , CommandResult , PlatformError } ,
2223 protocol:: { ReqRespCode , SpdmMsgHdr , SpdmVersion , NONCE_LEN } ,
2324 state:: ConnectionState ,
25+ transcript:: TranscriptContext ,
2426} ;
2527
2628/// Generate a GET_MEASUREMENTS request
@@ -31,10 +33,12 @@ use crate::{
3133/// * `raw_bitstream_requested`: Request a raw bit stream (if supported) (SPDM v1.2+)
3234/// * `new_measurement_requested`: Request new measurement if the responder has pending updates to blocks (SPDM v1.3+)
3335/// * `meas_op`: Measurement operation
34- /// (0x00: query total number of available blocks, 0x01-0xFE: query block, 0xFF: query all blocks)
3536/// * `slot_id`: Request a signed measurement if provided, signed with certificate slot identifier (0-7)
3637/// * `context`: Append optional 8 byte context (SPDM v1.3+)
3738///
39+ /// ## Note
40+ /// For SPDM version 1.0 this implementation does **not** supporte signed measurements.
41+ ///
3842/// # Returns
3943/// - () on success
4044/// - [CommandError] on failure
@@ -49,7 +53,7 @@ pub fn generate_get_measurements<'a>(
4953 req_buf : & mut MessageBuf < ' a > ,
5054 raw_bitstream_requested : bool ,
5155 new_measurement_requested : bool ,
52- meas_op : u8 ,
56+ meas_op : MeasurementOperation ,
5357 slot_id : Option < u8 > ,
5458 context : Option < & [ u8 ; 8 ] > ,
5559) -> CommandResult < ( ) > {
@@ -72,7 +76,11 @@ pub fn generate_get_measurements<'a>(
7276 let mut req_attr = GetMeasurementsReqAttr ( 0 ) ;
7377
7478 // signature requested is available in all versions
79+ // (v1.0 doesn't support slot-ids)
7580 if slot_id. is_some ( ) {
81+ if connection_version == SpdmVersion :: V10 {
82+ return Err ( ( true , CommandError :: UnsupportedRequest ) ) ;
83+ }
7684 // Error if the responder doesn't support this
7785 if !responder_supports_signed_measurements ( ctx) {
7886 return Err ( ( true , CommandError :: UnsupportedRequest ) ) ;
@@ -96,7 +104,10 @@ pub fn generate_get_measurements<'a>(
96104 }
97105
98106 // Encode request attributes and `Measurement` operation
99- let get_meas_common = GetMeasurementsReqCommon { req_attr, meas_op } ;
107+ let get_meas_common = GetMeasurementsReqCommon {
108+ req_attr,
109+ meas_op : meas_op. try_into ( ) . map_err ( |e| ( true , e) ) ?,
110+ } ;
100111 payload_len += get_meas_common
101112 . encode ( req_buf)
102113 . map_err ( |e| ( false , CommandError :: Codec ( e) ) ) ?;
@@ -137,7 +148,7 @@ pub fn generate_get_measurements<'a>(
137148 . push_data ( payload_len)
138149 . map_err ( |_| ( false , CommandError :: BufferTooSmall ) ) ?;
139150
140- ctx. append_message_to_transcript ( req_buf, crate :: transcript :: TranscriptContext :: L1 )
151+ ctx. append_message_to_transcript ( req_buf, TranscriptContext :: L1 )
141152}
142153
143154/// Check if the responder supports signing its measurements
@@ -165,5 +176,56 @@ pub(crate) fn handle_measurements_response<'a>(
165176 resp_header : SpdmMsgHdr ,
166177 resp : & mut MessageBuf < ' a > ,
167178) -> CommandResult < ( ) > {
168- todo ! ( )
179+ // Validate connection state - algorithms must be negotiated
180+ if ctx. state . connection_info . state ( ) < ConnectionState :: AlgorithmsNegotiated {
181+ return Err ( ( true , CommandError :: UnsupportedResponse ) ) ;
182+ }
183+
184+ // Validate version matches connection version
185+ let connection_version = ctx. state . connection_info . version_number ( ) ;
186+ if resp_header. version ( ) . ok ( ) != Some ( connection_version) {
187+ return Err ( ( true , CommandError :: InvalidResponse ) ) ;
188+ }
189+
190+ // Include the already parsed header again to parse `MeasurementsRspFixed`
191+ resp. push_data ( size_of :: < SpdmMsgHdr > ( ) )
192+ . map_err ( |e| ( false , e. into ( ) ) ) ?;
193+
194+ let fixed_fields = MeasurementsRspFixed :: decode ( resp) . map_err ( |e| ( true , e. into ( ) ) ) ?;
195+
196+ // Convert 3-byte measurement record length to u32
197+ let _meas_record_length = u32:: from_le_bytes ( [
198+ fixed_fields. measurement_record_len_byte0 ( ) ,
199+ fixed_fields. measurement_record_len_byte1 ( ) ,
200+ fixed_fields. measurement_record_len_byte2 ( ) ,
201+ 0 ,
202+ ] ) ;
203+
204+ // Decode all measurement blocks
205+ for _ in 0 ..fixed_fields. num_blocks ( ) {
206+ let block_header = MeasurementBlockHeader :: decode ( resp) . map_err ( |e| ( true , e. into ( ) ) ) ?;
207+ resp. pull_data ( block_header. measurement_size . get ( ) as usize )
208+ . map_err ( |e| ( true , e. into ( ) ) ) ?;
209+ }
210+
211+ // Decode Nonce
212+ let _nonce = resp. data ( 32 ) . map_err ( |e| ( true , e. into ( ) ) ) ?;
213+ resp. pull_data ( 32 ) . map_err ( |e| ( true , e. into ( ) ) ) ?;
214+
215+ // Decode opaque data
216+ let mut len_bytes = [ 0 ; 2 ] ;
217+ len_bytes. copy_from_slice ( resp. data ( 2 ) . map_err ( |e| ( true , e. into ( ) ) ) ?) ;
218+ let opaque_data_len = u16:: from_le_bytes ( len_bytes) ;
219+ resp. pull_data ( 2 ) . map_err ( |e| ( true , e. into ( ) ) ) ?;
220+ resp. pull_data ( opaque_data_len as usize )
221+ . map_err ( |e| ( true , e. into ( ) ) ) ?;
222+
223+ // Decode requester context
224+ let _requester_ctx = resp. data ( 8 ) . map_err ( |e| ( true , e. into ( ) ) ) ?;
225+ resp. pull_data ( 8 ) . map_err ( |e| ( true , e. into ( ) ) ) ?;
226+
227+ // Remaining is the signature, if requested by the GET_MEASUREMENTS request
228+
229+ // Append response to transcript (L1 context for measurements)
230+ ctx. append_message_to_transcript ( resp, TranscriptContext :: L1 )
169231}
0 commit comments