Coverage Report

Created: 2026-01-17 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spdm-rs/spdmlib/src/responder/finish_rsp.rs
Line
Count
Source
1
// Copyright (c) 2020 Intel Corporation
2
//
3
// SPDX-License-Identifier: Apache-2.0 or MIT
4
5
use crate::common::session::SpdmSession;
6
use crate::common::{ManagedBuffer12Sign, SpdmCodec, SpdmOpaqueStruct, MAX_SPDM_OPAQUE_SIZE};
7
use crate::crypto;
8
use crate::error::*;
9
use crate::message::*;
10
use crate::protocol::*;
11
use crate::responder::*;
12
extern crate alloc;
13
use alloc::boxed::Box;
14
15
impl ResponderContext {
16
0
    pub fn handle_spdm_finish<'a>(
17
0
        &mut self,
18
0
        session_id: u32,
19
0
        bytes: &[u8],
20
0
        writer: &'a mut Writer,
21
0
    ) -> (SpdmResult, Option<&'a [u8]>) {
22
        #[cfg(feature = "mandatory-mut-auth")]
23
        if !self.common.mut_auth_done {
24
            if let Some(session) = self.common.get_session_via_id(session_id) {
25
                session.teardown();
26
            }
27
            return (Ok(()), None);
28
        }
29
30
0
        let (result, rsp_slice) = self.write_spdm_finish_response(session_id, bytes, writer);
31
0
        if result.is_err() {
32
0
            if let Some(session) = self.common.get_session_via_id(session_id) {
33
0
                session.teardown();
34
0
            }
35
0
        }
36
37
0
        (result, rsp_slice)
38
0
    }
39
40
    // Return true on success, false otherwise.
41
0
    pub fn write_spdm_finish_response<'a>(
42
0
        &mut self,
43
0
        session_id: u32,
44
0
        bytes: &[u8],
45
0
        writer: &'a mut Writer,
46
0
    ) -> (SpdmResult, Option<&'a [u8]>) {
47
0
        let mut reader = Reader::init(bytes);
48
0
        let message_header = SpdmMessageHeader::read(&mut reader);
49
0
        if let Some(message_header) = message_header {
50
0
            if message_header.version != self.common.negotiate_info.spdm_version_sel {
51
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorVersionMismatch, 0, writer);
52
0
                return (
53
0
                    Err(SPDM_STATUS_INVALID_MSG_FIELD),
54
0
                    Some(writer.used_slice()),
55
0
                );
56
0
            }
57
        } else {
58
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorInvalidRequest, 0, writer);
59
0
            return (
60
0
                Err(SPDM_STATUS_INVALID_MSG_FIELD),
61
0
                Some(writer.used_slice()),
62
0
            );
63
        }
64
65
0
        self.common.reset_buffer_via_request_code(
66
0
            SpdmRequestResponseCode::SpdmRequestFinish,
67
0
            Some(session_id),
68
        );
69
70
0
        let finish_req = SpdmFinishRequestPayload::spdm_read(&mut self.common, &mut reader);
71
0
        if let Some(finish_req) = &finish_req {
72
0
            debug!("!!! finish req : {:02x?}\n", finish_req);
73
        } else {
74
0
            error!("!!! finish req : fail !!!\n");
75
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorInvalidRequest, 0, writer);
76
0
            return (
77
0
                Err(SPDM_STATUS_INVALID_MSG_FIELD),
78
0
                Some(writer.used_slice()),
79
0
            );
80
        }
81
0
        let finish_req = finish_req.unwrap();
82
83
0
        let opaque_total_size =
84
0
            if self.common.negotiate_info.spdm_version_sel >= SpdmVersion::SpdmVersion14 {
85
0
                2 + finish_req.opaque.data_size as usize
86
            } else {
87
0
                0usize
88
            };
89
90
0
        if self
91
0
            .common
92
0
            .append_message_f(false, session_id, &bytes[..4 + opaque_total_size])
93
0
            .is_err()
94
        {
95
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
96
0
            return (
97
0
                Err(SPDM_STATUS_INVALID_STATE_LOCAL),
98
0
                Some(writer.used_slice()),
99
0
            );
100
0
        }
101
102
0
        let mut_auth_attributes =
103
0
            if let Some(session) = self.common.get_immutable_session_via_id(session_id) {
104
0
                session.get_mut_auth_requested()
105
            } else {
106
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
107
0
                return (
108
0
                    Err(SPDM_STATUS_INVALID_STATE_LOCAL),
109
0
                    Some(writer.used_slice()),
110
0
                );
111
            };
112
113
0
        let finish_request_attributes = finish_req.finish_request_attributes;
114
115
0
        if (!mut_auth_attributes.is_empty()
116
0
            && !finish_request_attributes.contains(SpdmFinishRequestAttributes::SIGNATURE_INCLUDED))
117
0
            || (mut_auth_attributes.is_empty()
118
0
                && finish_request_attributes
119
0
                    .contains(SpdmFinishRequestAttributes::SIGNATURE_INCLUDED))
120
        {
121
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorInvalidRequest, 0, writer);
122
0
            return (
123
0
                Err(SPDM_STATUS_INVALID_MSG_FIELD),
124
0
                Some(writer.used_slice()),
125
0
            );
126
0
        }
127
128
0
        let is_mut_auth = !mut_auth_attributes.is_empty();
129
0
        if is_mut_auth {
130
0
            let session =
131
0
                if let Some(session) = self.common.get_immutable_session_via_id(session_id) {
132
0
                    session
133
                } else {
134
0
                    self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
135
0
                    return (
136
0
                        Err(SPDM_STATUS_INVALID_STATE_LOCAL),
137
0
                        Some(writer.used_slice()),
138
0
                    );
139
                };
140
141
0
            if self
142
0
                .verify_finish_req_signature(&finish_req.signature, session)
143
0
                .is_err()
144
            {
145
0
                error!("verify finish request signature error");
146
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorDecryptError, 0, writer);
147
0
                return (Err(SPDM_STATUS_CRYPTO_ERROR), Some(writer.used_slice()));
148
0
            }
149
0
            info!("verify_finish_req_signature pass");
150
151
0
            if self
152
0
                .common
153
0
                .append_message_f(false, session_id, finish_req.signature.as_ref())
154
0
                .is_err()
155
            {
156
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
157
0
                return (
158
0
                    Err(SPDM_STATUS_INVALID_STATE_LOCAL),
159
0
                    Some(writer.used_slice()),
160
0
                );
161
0
            }
162
0
        }
163
164
        // verify HMAC with finished_key
165
0
        let base_hash_size = self.common.get_hash_size() as usize;
166
167
        {
168
0
            let session = if let Some(session) = self.common.get_session_via_id(session_id) {
169
0
                session
170
            } else {
171
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
172
0
                return (
173
0
                    Err(SPDM_STATUS_INVALID_STATE_LOCAL),
174
0
                    Some(writer.used_slice()),
175
0
                );
176
            };
177
178
0
            if session.get_use_psk() {
179
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorInvalidRequest, 0, writer);
180
0
                return (
181
0
                    Err(SPDM_STATUS_INVALID_MSG_FIELD),
182
0
                    Some(writer.used_slice()),
183
0
                );
184
0
            }
185
186
0
            let session =
187
0
                if let Some(session) = self.common.get_immutable_session_via_id(session_id) {
188
0
                    session
189
                } else {
190
0
                    self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
191
0
                    return (
192
0
                        Err(SPDM_STATUS_INVALID_STATE_LOCAL),
193
0
                        Some(writer.used_slice()),
194
0
                    );
195
                };
196
197
0
            let transcript_hash = self
198
0
                .common
199
0
                .calc_rsp_transcript_hash(false, is_mut_auth, session);
200
0
            if transcript_hash.is_err() {
201
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
202
0
                return (Err(SPDM_STATUS_CRYPTO_ERROR), Some(writer.used_slice()));
203
0
            }
204
0
            let transcript_hash = transcript_hash.as_ref().unwrap();
205
206
0
            if session
207
0
                .verify_hmac_with_request_finished_key(
208
0
                    transcript_hash.as_ref(),
209
0
                    &finish_req.verify_data,
210
0
                )
211
0
                .is_err()
212
            {
213
0
                error!("verify_hmac_with_request_finished_key fail");
214
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorDecryptError, 0, writer);
215
0
                return (Err(SPDM_STATUS_CRYPTO_ERROR), Some(writer.used_slice()));
216
            } else {
217
0
                info!("verify_hmac_with_request_finished_key pass");
218
            }
219
220
0
            if self
221
0
                .common
222
0
                .append_message_f(false, session_id, finish_req.verify_data.as_ref())
223
0
                .is_err()
224
            {
225
0
                error!("message_f add the message error");
226
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
227
0
                return (
228
0
                    Err(SPDM_STATUS_INVALID_STATE_LOCAL),
229
0
                    Some(writer.used_slice()),
230
0
                );
231
0
            }
232
        }
233
234
0
        let in_clear_text = self
235
0
            .common
236
0
            .negotiate_info
237
0
            .req_capabilities_sel
238
0
            .contains(SpdmRequestCapabilityFlags::HANDSHAKE_IN_THE_CLEAR_CAP)
239
0
            && self
240
0
                .common
241
0
                .negotiate_info
242
0
                .rsp_capabilities_sel
243
0
                .contains(SpdmResponseCapabilityFlags::HANDSHAKE_IN_THE_CLEAR_CAP);
244
245
0
        info!("send spdm finish rsp\n");
246
247
0
        let response = SpdmMessage {
248
0
            header: SpdmMessageHeader {
249
0
                version: self.common.negotiate_info.spdm_version_sel,
250
0
                request_response_code: SpdmRequestResponseCode::SpdmResponseFinishRsp,
251
0
            },
252
0
            payload: SpdmMessagePayload::SpdmFinishResponse(SpdmFinishResponsePayload {
253
0
                verify_data: SpdmDigestStruct {
254
0
                    data_size: (self as &ResponderContext).common.get_hash_size(),
255
0
                    data: Box::new([0xcc; SPDM_MAX_HASH_SIZE]),
256
0
                },
257
0
                opaque: SpdmOpaqueStruct {
258
0
                    data_size: 0,
259
0
                    data: [0u8; MAX_SPDM_OPAQUE_SIZE],
260
0
                },
261
0
            }),
262
0
        };
263
264
0
        let res = response.spdm_encode(&mut self.common, writer);
265
0
        if res.is_err() {
266
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
267
0
            return (
268
0
                Err(SPDM_STATUS_INVALID_STATE_LOCAL),
269
0
                Some(writer.used_slice()),
270
0
            );
271
0
        }
272
0
        let used = writer.used();
273
274
0
        if in_clear_text {
275
            // generate HMAC with finished_key
276
0
            let temp_used = used - base_hash_size;
277
278
0
            if self
279
0
                .common
280
0
                .append_message_f(false, session_id, &writer.used_slice()[..temp_used])
281
0
                .is_err()
282
            {
283
0
                error!("message_f add the message error");
284
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
285
0
                return (
286
0
                    Err(SPDM_STATUS_INVALID_STATE_LOCAL),
287
0
                    Some(writer.used_slice()),
288
0
                );
289
0
            }
290
291
0
            let session =
292
0
                if let Some(session) = self.common.get_immutable_session_via_id(session_id) {
293
0
                    session
294
                } else {
295
0
                    self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
296
0
                    return (
297
0
                        Err(SPDM_STATUS_INVALID_STATE_LOCAL),
298
0
                        Some(writer.used_slice()),
299
0
                    );
300
                };
301
302
0
            let transcript_hash = self
303
0
                .common
304
0
                .calc_rsp_transcript_hash(false, is_mut_auth, session);
305
0
            if transcript_hash.is_err() {
306
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
307
0
                return (Err(SPDM_STATUS_CRYPTO_ERROR), Some(writer.used_slice()));
308
0
            }
309
0
            let transcript_hash = transcript_hash.unwrap();
310
311
0
            let hmac = session.generate_hmac_with_response_finished_key(transcript_hash.as_ref());
312
0
            if hmac.is_err() {
313
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
314
0
                return (Err(SPDM_STATUS_CRYPTO_ERROR), Some(writer.used_slice()));
315
0
            }
316
0
            let hmac = hmac.unwrap();
317
318
0
            if self
319
0
                .common
320
0
                .append_message_f(false, session_id, hmac.as_ref())
321
0
                .is_err()
322
            {
323
0
                self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
324
0
                return (
325
0
                    Err(SPDM_STATUS_INVALID_STATE_LOCAL),
326
0
                    Some(writer.used_slice()),
327
0
                );
328
0
            }
329
330
            // patch the message before send
331
0
            writer.mut_used_slice()[(used - base_hash_size)..used].copy_from_slice(hmac.as_ref());
332
0
        } else if self
333
0
            .common
334
0
            .append_message_f(false, session_id, &writer.used_slice()[..used])
335
0
            .is_err()
336
        {
337
0
            error!("message_f add the message error");
338
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
339
0
            return (
340
0
                Err(SPDM_STATUS_INVALID_STATE_LOCAL),
341
0
                Some(writer.used_slice()),
342
0
            );
343
0
        }
344
345
        // generate the data secret
346
0
        let session = if let Some(session) = self.common.get_immutable_session_via_id(session_id) {
347
0
            session
348
        } else {
349
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
350
0
            return (
351
0
                Err(SPDM_STATUS_INVALID_STATE_LOCAL),
352
0
                Some(writer.used_slice()),
353
0
            );
354
        };
355
356
0
        let th2 = self
357
0
            .common
358
0
            .calc_rsp_transcript_hash(false, is_mut_auth, session);
359
360
0
        if th2.is_err() {
361
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
362
0
            return (Err(SPDM_STATUS_CRYPTO_ERROR), Some(writer.used_slice()));
363
0
        }
364
0
        let th2 = th2.unwrap();
365
0
        debug!("!!! th2 : {:02x?}\n", th2.as_ref());
366
0
        let spdm_version_sel = self.common.negotiate_info.spdm_version_sel;
367
0
        let session = if let Some(session) = self.common.get_session_via_id(session_id) {
368
0
            session
369
        } else {
370
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
371
0
            return (
372
0
                Err(SPDM_STATUS_INVALID_STATE_LOCAL),
373
0
                Some(writer.used_slice()),
374
0
            );
375
        };
376
0
        session.set_th2(th2.clone());
377
0
        if let Err(e) = session.generate_data_secret(spdm_version_sel, &th2) {
378
0
            self.write_spdm_error(SpdmErrorCode::SpdmErrorUnspecified, 0, writer);
379
0
            (Err(e), Some(writer.used_slice()))
380
        } else {
381
0
            (Ok(()), Some(writer.used_slice()))
382
        }
383
0
    }
384
385
    #[cfg(not(feature = "hashed-transcript-data"))]
386
    fn verify_finish_req_signature(
387
        &self,
388
        signature: &SpdmSignatureStruct,
389
        session: &SpdmSession,
390
    ) -> SpdmResult {
391
        let transcript_data_hash = self.common.calc_rsp_transcript_hash(false, true, session)?;
392
393
        let peer_slot_id = self.common.runtime_info.get_peer_used_cert_chain_slot_id();
394
        let peer_cert = if peer_slot_id == SPDM_PUB_KEY_SLOT_ID_KEY_EXCHANGE_RSP {
395
            let peer_pub_key = self
396
                .common
397
                .provision_info
398
                .peer_pub_key
399
                .as_ref()
400
                .ok_or(SPDM_STATUS_INVALID_PARAMETER)?;
401
            &peer_pub_key.data[..peer_pub_key.data_size as usize]
402
        } else {
403
            &self.common.peer_info.peer_cert_chain[peer_slot_id as usize]
404
                .as_ref()
405
                .ok_or(SPDM_STATUS_INVALID_PARAMETER)?
406
                .data[(4usize + self.common.get_hash_size() as usize)
407
                ..(self.common.peer_info.peer_cert_chain[peer_slot_id as usize]
408
                    .as_ref()
409
                    .ok_or(SPDM_STATUS_INVALID_PARAMETER)?
410
                    .data_size as usize)]
411
        };
412
        let mut transcript_sign = ManagedBuffer12Sign::default();
413
        if self.common.negotiate_info.spdm_version_sel >= SpdmVersion::SpdmVersion12 {
414
            transcript_sign.reset_message();
415
            transcript_sign
416
                .append_message(&self.common.get_signing_prefix_context())
417
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
418
            transcript_sign
419
                .append_message(&SPDM_VERSION_SIGNING_CONTEXT_ZEROPAD_12)
420
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
421
            transcript_sign
422
                .append_message(&SPDM_FINISH_SIGN_CONTEXT)
423
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
424
            transcript_sign
425
                .append_message(transcript_data_hash.as_ref())
426
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
427
        }
428
429
        let peer_cert_der = if peer_slot_id == SPDM_PUB_KEY_SLOT_ID_KEY_EXCHANGE_RSP {
430
            SpdmDer::SpdmDerPubKeyRfc7250(peer_cert)
431
        } else {
432
            SpdmDer::SpdmDerCertChain(peer_cert)
433
        };
434
435
        crypto::spdm_asym_verify(
436
            self.common.negotiate_info.base_hash_sel,
437
            self.common.negotiate_info.req_asym_sel.to_base(),
438
            self.common.negotiate_info.pqc_req_asym_sel.to_base(),
439
            peer_cert_der,
440
            transcript_sign.as_ref(),
441
            signature,
442
        )
443
    }
444
445
    #[cfg(feature = "hashed-transcript-data")]
446
0
    fn verify_finish_req_signature(
447
0
        &self,
448
0
        signature: &SpdmSignatureStruct,
449
0
        session: &SpdmSession,
450
0
    ) -> SpdmResult {
451
0
        let transcript_hash = self.common.calc_rsp_transcript_hash(false, true, session)?;
452
453
0
        let peer_slot_id = self.common.runtime_info.get_peer_used_cert_chain_slot_id();
454
0
        let peer_cert = if peer_slot_id == SPDM_PUB_KEY_SLOT_ID_KEY_EXCHANGE_RSP {
455
0
            let peer_pub_key = self
456
0
                .common
457
0
                .provision_info
458
0
                .peer_pub_key
459
0
                .as_ref()
460
0
                .ok_or(SPDM_STATUS_INVALID_PARAMETER)?;
461
0
            &peer_pub_key.data[..peer_pub_key.data_size as usize]
462
        } else {
463
0
            &self.common.peer_info.peer_cert_chain[peer_slot_id as usize]
464
0
                .as_ref()
465
0
                .ok_or(SPDM_STATUS_INVALID_PARAMETER)?
466
0
                .data[(4usize + self.common.get_hash_size() as usize)
467
0
                ..(self.common.peer_info.peer_cert_chain[peer_slot_id as usize]
468
0
                    .as_ref()
469
0
                    .ok_or(SPDM_STATUS_INVALID_PARAMETER)?
470
                    .data_size as usize)]
471
        };
472
0
        let mut transcript_hash_sign = ManagedBuffer12Sign::default();
473
0
        if self.common.negotiate_info.spdm_version_sel >= SpdmVersion::SpdmVersion12 {
474
0
            transcript_hash_sign.reset_message();
475
0
            transcript_hash_sign
476
0
                .append_message(&self.common.get_signing_prefix_context())
477
0
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
478
0
            transcript_hash_sign
479
0
                .append_message(&SPDM_VERSION_SIGNING_CONTEXT_ZEROPAD_12)
480
0
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
481
0
            transcript_hash_sign
482
0
                .append_message(&SPDM_FINISH_SIGN_CONTEXT)
483
0
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
484
0
            transcript_hash_sign
485
0
                .append_message(transcript_hash.as_ref())
486
0
                .ok_or(SPDM_STATUS_BUFFER_FULL)?;
487
        } else {
488
0
            error!("hashed-transcript-data is unsupported in SPDM 1.0/1.1 signing!\n");
489
0
            return Err(SPDM_STATUS_INVALID_STATE_LOCAL);
490
        }
491
492
0
        let peer_cert_der = if peer_slot_id == SPDM_PUB_KEY_SLOT_ID_KEY_EXCHANGE_RSP {
493
0
            SpdmDer::SpdmDerPubKeyRfc7250(peer_cert)
494
        } else {
495
0
            SpdmDer::SpdmDerCertChain(peer_cert)
496
        };
497
498
0
        let res = crypto::spdm_asym_verify(
499
0
            self.common.negotiate_info.base_hash_sel,
500
0
            self.common.negotiate_info.req_asym_sel.to_base(),
501
0
            self.common.negotiate_info.pqc_req_asym_sel.to_base(),
502
0
            peer_cert_der,
503
0
            transcript_hash_sign.as_ref(),
504
0
            signature,
505
        );
506
507
0
        res
508
0
    }
509
}