Skip to content

Commit 11a7af3

Browse files
committed
wip
Signed-off-by: leongross <leon.gross@9elements.com>
1 parent 6d0218a commit 11a7af3

File tree

21 files changed

+777
-117
lines changed

21 files changed

+777
-117
lines changed

examples/spdm_requester.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ use std::net::TcpStream;
99
use clap::Parser;
1010
use der::{Decode, Encode};
1111
use p384::ecdsa::{Signature, VerifyingKey};
12+
use spdm_lib::chunk_ctx::ChunkError;
1213
use spdm_lib::codec::MessageBuf;
1314
use spdm_lib::commands::certificate::request::generate_get_certificate;
1415
use spdm_lib::commands::challenge::{
1516
request::generate_challenge_request, MeasurementSummaryHashType,
1617
};
1718
use spdm_lib::context::SpdmContext;
18-
use spdm_lib::error::SpdmError;
19+
use spdm_lib::error::{CommandError, SpdmError};
1920
use spdm_lib::protocol::algorithms::{
2021
AeadCipherSuite, AlgorithmPriorityTable, BaseAsymAlgo, BaseHashAlgo, DeviceAlgorithms,
2122
DheNamedGroup, KeySchedule, LocalDeviceAlgorithms, MeasurementHashAlgo,
@@ -216,8 +217,12 @@ fn full_flow(stream: TcpStream, config: &RequesterConfig) -> IoResult<()> {
216217
}
217218

218219
// Process SPDM messages using the context
219-
let mut buffer = [0u8; 4096];
220+
// let mut buffer = [0u8; capabilities.data_transfer_size];
221+
let mut buffer = [0u8; 1024];
220222
let mut message_buffer = MessageBuf::new(&mut buffer);
223+
224+
let mut large_buffer = [0u8; 8192];
225+
let mut large_response_buffer = MessageBuf::new(&mut large_buffer);
221226
// For now, we just want to show, that the VCA (Version, Capability, Auth) flow works as expected
222227
// For that, we need to do the following:
223228
// 1.1 Send GET_VERSION
@@ -267,6 +272,24 @@ fn full_flow(stream: TcpStream, config: &RequesterConfig) -> IoResult<()> {
267272
.requester_send_request(&mut message_buffer, EID)
268273
.unwrap();
269274

275+
// CAPABILITES is the first response that might require chunking!
276+
match spdm_context.requester_process_message(&mut message_buffer) {
277+
// For now ig we have to live with it like this.
278+
// An alternative would be to poll our own context to check if the chunking
279+
// was initialized and then proceed with the chunk retrieval and assembly.
280+
Err(SpdmError::Command(CommandError::Chunk(ChunkError::None))) => {
281+
message_buffer.reset();
282+
spdm_context
283+
.requester_retrieve_large_response(
284+
&mut message_buffer,
285+
EID,
286+
&mut large_response_buffer,
287+
)
288+
.unwrap();
289+
}
290+
e => e.unwrap(),
291+
};
292+
270293
if config.verbose {
271294
println!(
272295
"Sent GET_CAPABILITIES: {:?}",
@@ -487,6 +510,22 @@ fn full_flow(stream: TcpStream, config: &RequesterConfig) -> IoResult<()> {
487510
println!("CHALLENGE_AUTH signature verification successfull");
488511
}
489512

513+
// Used for GET_MEASUREMENTS.
514+
// match spdm_context.requester_process_message(&mut message_buffer) {
515+
// // For now ig we have to live with it like this.
516+
// // An alternative would be to poll our own context to check if the chunking
517+
// // was initialized and then proceed with the chunk retrieval and assembly.
518+
// SpdmError::Command(CommandError::Chunk(ChunkError::None)) => {
519+
// message_buffer.reset();
520+
// spdm_context.requester_retrieve_large_response(
521+
// &mut message_buffer,
522+
// EID,
523+
// &mut large_response_buffer,
524+
// )?;
525+
// }
526+
// SpdmError(e) => e,
527+
// };
528+
490529
Ok(())
491530
}
492531

src/chunk_ctx.rs

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

55
#[derive(Debug, PartialEq)]
66
pub enum ChunkError {
7+
/// Super hacky way to signal back the client application via [CommandError::Chunk(ChunkError::None)]
8+
/// that the chunking process has started and that it now has to retrieve the chunked data.
9+
None,
710
LargeResponseInitError,
811
NoLargeResponseInProgress,
912
InvalidChunkHandle,
@@ -22,6 +25,11 @@ struct ChunkInfo {
2225
}
2326

2427
impl ChunkInfo {
28+
/// Reset the chunk info.
29+
///
30+
/// # Arguments
31+
/// - `reset_handle`: If `reset_handle` is set, the handle of the chunk info
32+
/// is set to `0`. If not, it is incremented to be ready for the next chunk operation.
2533
pub fn reset(&mut self, reset_handle: bool) {
2634
self.chunk_in_use = false;
2735
if reset_handle {
@@ -52,7 +60,18 @@ pub(crate) enum LargeResponse {
5260
Measurements(MeasurementsResponse),
5361
}
5462

55-
/// Manages the context for ongoing large message responses
63+
/// Manages the context for ongoing large message responses.
64+
/// The naming might be confusion, but we re-use this struct for both requester and responder.
65+
///
66+
/// # Requester
67+
///
68+
/// For the requester, this [LargeResponseCtx] is used to track the state
69+
/// of `CHUNK_SEND` commands.
70+
///
71+
/// # Responder
72+
///
73+
/// For the responder, this [LargeResponseCtx] is used to track the state
74+
/// of `CHUNK_RESPONSE` commands.
5675
#[derive(Default)]
5776
pub(crate) struct LargeResponseCtx {
5877
chunk_info: ChunkInfo,
@@ -115,4 +134,82 @@ impl LargeResponseCtx {
115134
pub fn bytes_transferred(&self) -> usize {
116135
self.chunk_info.bytes_transferred
117136
}
137+
138+
pub fn seq_no(&self) -> u16 {
139+
self.chunk_info.chunk_seq_num
140+
}
141+
142+
pub fn handle(&self) -> u8 {
143+
self.chunk_info.chunk_handle
144+
}
145+
}
146+
147+
/// Manages the context for ongoing large message retrievals on the **requester** side.
148+
///
149+
/// Initialized when the requester receives `ERROR(LargeResponse)` and tracks
150+
/// progress across successive `CHUNK_GET` / `CHUNK_RESPONSE` exchanges until
151+
/// the last chunk has been received.
152+
#[derive(Default)]
153+
pub(crate) struct LargeRequestCtx {
154+
chunk_info: ChunkInfo,
155+
}
156+
157+
impl LargeRequestCtx {
158+
/// Initialize context for a new large response retrieval.
159+
///
160+
/// Call this when `ERROR(LargeResponse)` is received.
161+
///
162+
/// # Arguments
163+
/// * `handle` - The handle value carried in `Param2` of the `ERROR` message.
164+
pub(crate) fn init(&mut self, handle: u8) {
165+
// Total size is unknown until the first CHUNK_RESPONSE arrives.
166+
self.chunk_info.init(0, Some(handle));
167+
}
168+
169+
/// Discard any in-progress retrieval and reset to the initial state.
170+
pub(crate) fn reset(&mut self) {
171+
self.chunk_info.reset(true);
172+
}
173+
174+
/// Returns `true` if a large response retrieval is currently in progress.
175+
pub(crate) fn in_progress(&self) -> bool {
176+
self.chunk_info.chunk_in_use
177+
}
178+
179+
/// Returns the handle identifying the current large response transfer.
180+
pub(crate) fn handle(&self) -> u8 {
181+
self.chunk_info.chunk_handle
182+
}
183+
184+
/// Returns the `ChunkSeqNo` that shall appear in the next `CHUNK_GET`.
185+
pub(crate) fn current_seq_num(&self) -> u16 {
186+
self.chunk_info.chunk_seq_num
187+
}
188+
189+
/// Returns the number of bytes successfully received so far.
190+
pub(crate) fn bytes_received(&self) -> usize {
191+
self.chunk_info.bytes_transferred
192+
}
193+
194+
/// Returns the total size of the large response.
195+
///
196+
/// Only meaningful after the first `CHUNK_RESPONSE` (`ChunkSeqNo == 0`)
197+
/// has been processed and [`set_total_size`] has been called.
198+
pub(crate) fn total_size(&self) -> usize {
199+
self.chunk_info.large_msg_size
200+
}
201+
202+
/// Record the total response size decoded from the first `CHUNK_RESPONSE`.
203+
pub(crate) fn set_total_size(&mut self, size: usize) {
204+
self.chunk_info.large_msg_size = size;
205+
}
206+
207+
/// Advance tracking state after a chunk has been successfully received.
208+
///
209+
/// Adds `chunk_size` to `bytes_received` and bumps the sequence number so
210+
/// that the next `CHUNK_GET` requests the following chunk.
211+
pub(crate) fn advance(&mut self, chunk_size: usize) {
212+
self.chunk_info.bytes_transferred += chunk_size;
213+
self.chunk_info.chunk_seq_num = self.chunk_info.chunk_seq_num.wrapping_add(1);
214+
}
118215
}

src/commands/algorithms/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::{
44
codec::{Codec, MessageBuf},
55
commands::algorithms::{AlgStructure, AlgorithmsResp, ExtendedAlgo, NegotiateAlgorithmsReq},
6-
commands::error_rsp::ErrorCode,
6+
commands::error::ErrorCode,
77
context::SpdmContext,
88
error::{CommandError, CommandResult},
99
protocol::{DeviceAlgorithms, SpdmMsgHdr},

src/commands/algorithms/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
transcript::TranscriptContext,
1111
};
1212

13-
use crate::commands::error_rsp::ErrorCode;
13+
use crate::commands::error::ErrorCode;
1414
use crate::protocol::*;
1515

1616
use core::mem::size_of;

src/commands/capabilities/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Licensed under the Apache-2.0 license
22

3-
use crate::commands::error_rsp::ErrorCode;
3+
use crate::commands::error::ErrorCode;
44
use crate::protocol::CapabilityFlags;
55
use crate::{codec::MessageBuf, context::SpdmContext, error::CommandResult, protocol::SpdmMsgHdr};
66

src/commands/capabilities/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Licensed under the Apache-2.0 license
22
use super::*;
33
use crate::codec::{Codec, MessageBuf};
4-
use crate::commands::error_rsp::ErrorCode;
4+
use crate::commands::error::ErrorCode;
55
use crate::context::SpdmContext;
66
use crate::error::{CommandError, CommandResult};
77
use crate::protocol::*;

src/commands/certificate/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::commands::certificate::{
66
encode_certchain_metadata, CertificateRespAttributes, CertificateRespCommon, GetCertificateReq,
77
SlotId,
88
};
9-
use crate::commands::error_rsp::ErrorCode;
9+
use crate::commands::error::ErrorCode;
1010
use crate::context::SpdmContext;
1111
use crate::error::{CommandError, CommandResult};
1212
use crate::protocol::*;

src/commands/challenge/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::codec::{Codec, MessageBuf};
44
use crate::commands::algorithms::selected_measurement_specification;
55
use crate::commands::challenge::{ChallengeAuthRspBase, ChallengeReq, MeasurementSummaryHashType};
66
use crate::commands::digests::compute_cert_chain_hash;
7-
use crate::commands::error_rsp::ErrorCode;
7+
use crate::commands::error::ErrorCode;
88
use crate::context::SpdmContext;
99
use crate::error::{CommandError, CommandResult, PlatformError};
1010
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)