Skip to content

Commit 95645ff

Browse files
committed
wip
Signed-off-by: leongross <leon.gross@9elements.com>
1 parent 1e0ad41 commit 95645ff

File tree

22 files changed

+816
-115
lines changed

22 files changed

+816
-115
lines changed

examples/spdm_requester.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ use std::net::TcpStream;
2121
use clap::Parser;
2222
use der::{Decode, Encode};
2323
use p384::ecdsa::{Signature, VerifyingKey};
24+
use spdm_lib::chunk_ctx::ChunkError;
2425
use spdm_lib::codec::MessageBuf;
2526
use spdm_lib::commands::certificate::request::generate_get_certificate;
2627
use spdm_lib::commands::challenge::{
2728
request::generate_challenge_request, MeasurementSummaryHashType,
2829
};
2930
use spdm_lib::context::SpdmContext;
30-
use spdm_lib::error::SpdmError;
31+
use spdm_lib::error::{CommandError, SpdmError};
3132
use spdm_lib::protocol::algorithms::{
3233
AeadCipherSuite, AlgorithmPriorityTable, BaseAsymAlgo, BaseHashAlgo, DeviceAlgorithms,
3334
DheNamedGroup, KeySchedule, LocalDeviceAlgorithms, MeasurementHashAlgo,
@@ -228,8 +229,12 @@ fn full_flow(stream: TcpStream, config: &RequesterConfig) -> IoResult<()> {
228229
}
229230

230231
// Process SPDM messages using the context
231-
let mut buffer = [0u8; 4096];
232+
// let mut buffer = [0u8; capabilities.data_transfer_size];
233+
let mut buffer = [0u8; 1024];
232234
let mut message_buffer = MessageBuf::new(&mut buffer);
235+
236+
let mut large_buffer = [0u8; 8192];
237+
let mut large_response_buffer = MessageBuf::new(&mut large_buffer);
233238
// For now, we just want to show, that the VCA (Version, Capability, Auth) flow works as expected
234239
// For that, we need to do the following:
235240
// 1.1 Send GET_VERSION
@@ -279,6 +284,24 @@ fn full_flow(stream: TcpStream, config: &RequesterConfig) -> IoResult<()> {
279284
.requester_send_request(&mut message_buffer, EID)
280285
.unwrap();
281286

287+
// CAPABILITES is the first response that might require chunking!
288+
match spdm_context.requester_process_message(&mut message_buffer) {
289+
// For now ig we have to live with it like this.
290+
// An alternative would be to poll our own context to check if the chunking
291+
// was initialized and then proceed with the chunk retrieval and assembly.
292+
Err(SpdmError::Command(CommandError::Chunk(ChunkError::None))) => {
293+
message_buffer.reset();
294+
spdm_context
295+
.requester_retrieve_large_response(
296+
&mut message_buffer,
297+
EID,
298+
&mut large_response_buffer,
299+
)
300+
.unwrap();
301+
}
302+
e => e.unwrap(),
303+
};
304+
282305
if config.verbose {
283306
println!(
284307
"Sent GET_CAPABILITIES: {:?}",
@@ -499,6 +522,22 @@ fn full_flow(stream: TcpStream, config: &RequesterConfig) -> IoResult<()> {
499522
println!("CHALLENGE_AUTH signature verification successfull");
500523
}
501524

525+
// Used for GET_MEASUREMENTS.
526+
// match spdm_context.requester_process_message(&mut message_buffer) {
527+
// // For now ig we have to live with it like this.
528+
// // An alternative would be to poll our own context to check if the chunking
529+
// // was initialized and then proceed with the chunk retrieval and assembly.
530+
// SpdmError::Command(CommandError::Chunk(ChunkError::None)) => {
531+
// message_buffer.reset();
532+
// spdm_context.requester_retrieve_large_response(
533+
// &mut message_buffer,
534+
// EID,
535+
// &mut large_response_buffer,
536+
// )?;
537+
// }
538+
// SpdmError(e) => e,
539+
// };
540+
502541
Ok(())
503542
}
504543

src/chunk_ctx.rs

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ use crate::commands::measurements_rsp::MeasurementsResponse;
1616

1717
#[derive(Debug, PartialEq)]
1818
pub enum ChunkError {
19+
/// Super hacky way to signal back the client application via [CommandError::Chunk(ChunkError::None)]
20+
/// that the chunking process has started and that it now has to retrieve the chunked data.
21+
None,
1922
LargeResponseInitError,
2023
NoLargeResponseInProgress,
2124
InvalidChunkHandle,
@@ -34,6 +37,11 @@ struct ChunkInfo {
3437
}
3538

3639
impl ChunkInfo {
40+
/// Reset the chunk info.
41+
///
42+
/// # Arguments
43+
/// - `reset_handle`: If `reset_handle` is set, the handle of the chunk info
44+
/// is set to `0`. If not, it is incremented to be ready for the next chunk operation.
3745
pub fn reset(&mut self, reset_handle: bool) {
3846
self.chunk_in_use = false;
3947
if reset_handle {
@@ -64,7 +72,18 @@ pub(crate) enum LargeResponse {
6472
Measurements(MeasurementsResponse),
6573
}
6674

67-
/// Manages the context for ongoing large message responses
75+
/// Manages the context for ongoing large message responses.
76+
/// The naming might be confusion, but we re-use this struct for both requester and responder.
77+
///
78+
/// # Requester
79+
///
80+
/// For the requester, this [LargeResponseCtx] is used to track the state
81+
/// of `CHUNK_SEND` commands.
82+
///
83+
/// # Responder
84+
///
85+
/// For the responder, this [LargeResponseCtx] is used to track the state
86+
/// of `CHUNK_RESPONSE` commands.
6887
#[derive(Default)]
6988
pub(crate) struct LargeResponseCtx {
7089
chunk_info: ChunkInfo,
@@ -127,4 +146,82 @@ impl LargeResponseCtx {
127146
pub fn bytes_transferred(&self) -> usize {
128147
self.chunk_info.bytes_transferred
129148
}
149+
150+
pub fn seq_no(&self) -> u16 {
151+
self.chunk_info.chunk_seq_num
152+
}
153+
154+
pub fn handle(&self) -> u8 {
155+
self.chunk_info.chunk_handle
156+
}
157+
}
158+
159+
/// Manages the context for ongoing large message retrievals on the **requester** side.
160+
///
161+
/// Initialized when the requester receives `ERROR(LargeResponse)` and tracks
162+
/// progress across successive `CHUNK_GET` / `CHUNK_RESPONSE` exchanges until
163+
/// the last chunk has been received.
164+
#[derive(Default)]
165+
pub(crate) struct LargeRequestCtx {
166+
chunk_info: ChunkInfo,
167+
}
168+
169+
impl LargeRequestCtx {
170+
/// Initialize context for a new large response retrieval.
171+
///
172+
/// Call this when `ERROR(LargeResponse)` is received.
173+
///
174+
/// # Arguments
175+
/// * `handle` - The handle value carried in `Param2` of the `ERROR` message.
176+
pub(crate) fn init(&mut self, handle: u8) {
177+
// Total size is unknown until the first CHUNK_RESPONSE arrives.
178+
self.chunk_info.init(0, Some(handle));
179+
}
180+
181+
/// Discard any in-progress retrieval and reset to the initial state.
182+
pub(crate) fn reset(&mut self) {
183+
self.chunk_info.reset(true);
184+
}
185+
186+
/// Returns `true` if a large response retrieval is currently in progress.
187+
pub(crate) fn in_progress(&self) -> bool {
188+
self.chunk_info.chunk_in_use
189+
}
190+
191+
/// Returns the handle identifying the current large response transfer.
192+
pub(crate) fn handle(&self) -> u8 {
193+
self.chunk_info.chunk_handle
194+
}
195+
196+
/// Returns the `ChunkSeqNo` that shall appear in the next `CHUNK_GET`.
197+
pub(crate) fn current_seq_num(&self) -> u16 {
198+
self.chunk_info.chunk_seq_num
199+
}
200+
201+
/// Returns the number of bytes successfully received so far.
202+
pub(crate) fn bytes_received(&self) -> usize {
203+
self.chunk_info.bytes_transferred
204+
}
205+
206+
/// Returns the total size of the large response.
207+
///
208+
/// Only meaningful after the first `CHUNK_RESPONSE` (`ChunkSeqNo == 0`)
209+
/// has been processed and [`set_total_size`] has been called.
210+
pub(crate) fn total_size(&self) -> usize {
211+
self.chunk_info.large_msg_size
212+
}
213+
214+
/// Record the total response size decoded from the first `CHUNK_RESPONSE`.
215+
pub(crate) fn set_total_size(&mut self, size: usize) {
216+
self.chunk_info.large_msg_size = size;
217+
}
218+
219+
/// Advance tracking state after a chunk has been successfully received.
220+
///
221+
/// Adds `chunk_size` to `bytes_received` and bumps the sequence number so
222+
/// that the next `CHUNK_GET` requests the following chunk.
223+
pub(crate) fn advance(&mut self, chunk_size: usize) {
224+
self.chunk_info.bytes_transferred += chunk_size;
225+
self.chunk_info.chunk_seq_num = self.chunk_info.chunk_seq_num.wrapping_add(1);
226+
}
130227
}

src/commands/algorithms/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use crate::{
1616
codec::{Codec, MessageBuf},
1717
commands::algorithms::{AlgStructure, AlgorithmsResp, ExtendedAlgo, NegotiateAlgorithmsReq},
18-
commands::error_rsp::ErrorCode,
18+
commands::error::ErrorCode,
1919
context::SpdmContext,
2020
error::{CommandError, CommandResult},
2121
protocol::{DeviceAlgorithms, SpdmMsgHdr},

src/commands/algorithms/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{
2222
transcript::TranscriptContext,
2323
};
2424

25-
use crate::commands::error_rsp::ErrorCode;
25+
use crate::commands::error::ErrorCode;
2626
use crate::protocol::*;
2727

2828
use core::mem::size_of;

src/commands/capabilities/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use crate::commands::error_rsp::ErrorCode;
15+
use crate::commands::error::ErrorCode;
1616
use crate::protocol::CapabilityFlags;
1717
use crate::{codec::MessageBuf, context::SpdmContext, error::CommandResult, protocol::SpdmMsgHdr};
1818

src/commands/capabilities/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// limitations under the License.
1414
use super::*;
1515
use crate::codec::{Codec, MessageBuf};
16-
use crate::commands::error_rsp::ErrorCode;
16+
use crate::commands::error::ErrorCode;
1717
use crate::context::SpdmContext;
1818
use crate::error::{CommandError, CommandResult};
1919
use crate::protocol::*;

src/commands/certificate/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::commands::certificate::{
1818
encode_certchain_metadata, CertificateRespAttributes, CertificateRespCommon, GetCertificateReq,
1919
SlotId,
2020
};
21-
use crate::commands::error_rsp::ErrorCode;
21+
use crate::commands::error::ErrorCode;
2222
use crate::context::SpdmContext;
2323
use crate::error::{CommandError, CommandResult};
2424
use crate::protocol::*;

src/commands/challenge/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::codec::{Codec, MessageBuf};
1616
use crate::commands::algorithms::selected_measurement_specification;
1717
use crate::commands::challenge::{ChallengeAuthRspBase, ChallengeReq, MeasurementSummaryHashType};
1818
use crate::commands::digests::compute_cert_chain_hash;
19-
use crate::commands::error_rsp::ErrorCode;
19+
use crate::commands::error::ErrorCode;
2020
use crate::context::SpdmContext;
2121
use crate::error::{CommandError, CommandResult, PlatformError};
2222
use crate::platform::hash::SpdmHashAlgoType;

src/commands/chunk/mod.rs

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub(crate) struct ChunkGet {
3333
}
3434
impl CommonCodec for ChunkGet {}
3535

36+
impl ChunkGet {}
3637
#[derive(FromBytes, IntoBytes, Immutable)]
3738
#[repr(C, packed)]
3839
/// The fixed fields of the `CHUNK_RESPONSE` message. The actual response payload
@@ -57,6 +58,29 @@ struct ChunkResponseFixed {
5758
}
5859
impl CommonCodec for ChunkResponseFixed {}
5960

61+
impl ChunkResponseFixed {
62+
// Computes the chunk size based on the context and the chunk sequence number
63+
// Returns the chunk size and a boolean indicating if this is the last chunk
64+
pub(crate) fn compute_chunk_size(ctx: &SpdmContext, chunk_seq_num: u16) -> (usize, bool) {
65+
let extra_field_size = if chunk_seq_num == 0 {
66+
size_of::<LargeResponseSize>()
67+
} else {
68+
0
69+
};
70+
let chunk_size = ctx
71+
.min_data_transfer_size()
72+
.saturating_sub(size_of::<SpdmMsgHdr>() + size_of::<Self>() + extra_field_size);
73+
74+
let (is_last_chunk, remaining_len) = ctx.large_resp_context.last_chunk(chunk_size);
75+
76+
if is_last_chunk {
77+
(remaining_len, true)
78+
} else {
79+
(chunk_size, false)
80+
}
81+
}
82+
}
83+
6084
bitfield! {
6185
#[derive(FromBytes, IntoBytes, Immutable)]
6286
#[repr(C)]
@@ -90,10 +114,10 @@ struct LargeResponseSize(u32);
90114
impl CommonCodec for LargeResponseSize {}
91115

92116
#[derive(FromBytes, IntoBytes, Immutable)]
93-
#[repr(C)]
117+
#[repr(C, packed)]
94118
/// The fixed size components of the `CHUNK_SEND` request.
95119
/// When sent, this struct is followed by
96-
/// - `LargeMessageSize` field (only present in the first chunk, i.e., when `ChunkSeqNo` is 0)
120+
/// - `LargeMessageSize` field (only present in the first chunk, i.e., when `ChunkSeqNo` is 0). See [LargeMessageSize].
97121
pub(crate) struct ChunkSendFixed {
98122
param1: ChunkSenderAttr,
99123

@@ -119,16 +143,39 @@ impl ChunkSendFixed {
119143
chunk_size,
120144
}
121145
}
146+
147+
// Computes the chunk size based on the context and the chunk sequence number
148+
// Returns the chunk size and a boolean indicating if this is the last chunk
149+
pub(crate) fn compute_chunk_size(ctx: &SpdmContext, chunk_seq_num: u16) -> (usize, bool) {
150+
let extra_field_size = if chunk_seq_num == 0 {
151+
size_of::<LargeMessageSize>()
152+
} else {
153+
0
154+
};
155+
let chunk_size = ctx
156+
.min_data_transfer_size()
157+
.saturating_sub(size_of::<SpdmMsgHdr>() + size_of::<Self>() + extra_field_size);
158+
159+
let (is_last_chunk, remaining_len) = ctx.large_resp_context.last_chunk(chunk_size);
160+
161+
if is_last_chunk {
162+
(remaining_len, true)
163+
} else {
164+
(chunk_size, false)
165+
}
166+
}
122167
}
123168

169+
impl CommonCodec for ChunkSendFixed {}
170+
124171
#[derive(FromBytes, IntoBytes, Immutable)]
125172
#[repr(C)]
126173
/// Size of the large SPDM message being transferred. This field shall only be present when ChunkSeqNo is zero and shall have a non-zero value. Shall be greater than the DataTransferSize of the receiving SPDM endpoint.
127174
struct LargeMessageSize(u32);
128175
impl CommonCodec for LargeMessageSize {}
129176

130177
#[derive(FromBytes, IntoBytes, Immutable)]
131-
#[repr(C)]
178+
#[repr(C, packed)]
132179
/// # Note
133180
/// This struct may be followed by the variable length field `ResponseToLargeRequest`.
134181
/// `ResponseToLargeRequest` shall be present on the last chunk (that is, when LastChunk is set),
@@ -156,24 +203,3 @@ pub(crate) fn max_chunked_resp_size(ctx: &SpdmContext) -> usize {
156203
(min_data_transfer_size).saturating_sub(fixed_chunk_resp_size) * MAX_NUM_CHUNKS as usize
157204
- size_of::<u32>()
158205
}
159-
160-
// Computes the chunk size based on the context and the chunk sequence number
161-
// Returns the chunk size and a boolean indicating if this is the last chunk
162-
fn compute_chunk_size(ctx: &SpdmContext, chunk_seq_num: u16) -> (usize, bool) {
163-
let extra_field_size = if chunk_seq_num == 0 {
164-
size_of::<LargeResponseSize>()
165-
} else {
166-
0
167-
};
168-
let chunk_size = ctx.min_data_transfer_size().saturating_sub(
169-
size_of::<SpdmMsgHdr>() + size_of::<ChunkResponseFixed>() + extra_field_size,
170-
);
171-
172-
let (is_last_chunk, remaining_len) = ctx.large_resp_context.last_chunk(chunk_size);
173-
174-
if is_last_chunk {
175-
(remaining_len, true)
176-
} else {
177-
(chunk_size, false)
178-
}
179-
}

0 commit comments

Comments
 (0)