Skip to content

Commit 82ba9ef

Browse files
committed
fix: reject unchallenged response chunks
1 parent c0520b0 commit 82ba9ef

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

crates/cac/protocol/src/evaluator/stf.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use mosaic_cac_types::{
66
ChallengeResponseMsgChunk, ChallengeResponseMsgHeader, CommitMsgChunk, CommitMsgHeader,
77
CompletedSignatures, DepositAdaptors, DepositId, GarblingTableCommitment, HeapArray, Index,
88
OpenedGarblingTableCommitments, PubKey, ReservedSetupInputShares, Seed, SetupInputs,
9-
TableTransferReceiptMsg, TableTransferRequestMsg, WideLabelWirePolynomialCommitments,
9+
TableTransferReceiptMsg, WideLabelWirePolynomialCommitments,
1010
WideLabelZerothPolynomialCoefficients, WithdrawalAdaptors, WithdrawalAdaptorsChunk,
1111
WithdrawalInputs, state_machine::evaluator::*,
1212
};
@@ -807,6 +807,18 @@ async fn handle_recv_challenge_response_msg<S: StateMut>(
807807
header,
808808
remaining_chunks,
809809
} => {
810+
let challenge_idxs = state
811+
.get_challenge_indices()
812+
.await
813+
.require("expected challenge indices")?;
814+
815+
if !challenge_idxs
816+
.iter()
817+
.any(|idx| idx.get() == response_msg_chunk.circuit_index as usize)
818+
{
819+
return Err(SMError::InvalidInputData);
820+
}
821+
810822
let chunk_idx = (response_msg_chunk.circuit_index as usize)
811823
.checked_sub(1)
812824
.unwrap();
@@ -815,8 +827,11 @@ async fn handle_recv_challenge_response_msg<S: StateMut>(
815827
// expected chunk
816828
}
817829
Some(false) => {
818-
// already seen chunk
819-
warn!(%chunk_idx, "evaluator received duplicate commit chunk, ack and ignore");
830+
// already seen expected chunk
831+
warn!(
832+
%chunk_idx,
833+
"evaluator received duplicate challenge response chunk, ack and ignore"
834+
);
820835
return Ok(());
821836
}
822837
None => {

crates/cac/protocol/src/evaluator/tests.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,10 @@ async fn duplicate_challenge_response_chunk_is_ack_and_ignore() {
396396
// Mark first challenge index's chunk as already received.
397397
let first_challenge_idx = challenge_indices[0].get() - 1;
398398
remaining[first_challenge_idx] = false;
399+
state
400+
.put_challenge_indices(&challenge_indices)
401+
.await
402+
.unwrap();
399403
state
400404
.put_root_state(&EvaluatorState {
401405
config: None,
@@ -422,6 +426,41 @@ async fn duplicate_challenge_response_chunk_is_ack_and_ignore() {
422426
assert!(actions.is_empty(), "should produce no actions");
423427
}
424428

429+
#[tokio::test]
430+
async fn unchallenged_in_range_challenge_response_chunk_is_invalid() {
431+
let mut state = StoredEvaluatorState::default();
432+
let challenge_indices = ChallengeIndices::new(|i| Index::new((i * 2) + 1).unwrap());
433+
let remaining = get_remaining_challenge_response_chunks(&challenge_indices);
434+
state
435+
.put_challenge_indices(&challenge_indices)
436+
.await
437+
.unwrap();
438+
state
439+
.put_root_state(&EvaluatorState {
440+
config: None,
441+
step: Step::WaitingForChallengeResponse {
442+
header: false,
443+
remaining_chunks: remaining,
444+
},
445+
})
446+
.await
447+
.unwrap();
448+
449+
let mut actions = Vec::new();
450+
let result = handle_event(
451+
&mut state,
452+
Input::RecvChallengeResponseMsgChunk(dummy_challenge_response_chunk(2)),
453+
&mut actions,
454+
)
455+
.await;
456+
457+
assert!(
458+
matches!(result, Err(crate::error::SMError::InvalidInputData)),
459+
"unchallenged in-range chunk must be rejected, got: {result:?}"
460+
);
461+
assert!(actions.is_empty(), "should produce no actions");
462+
}
463+
425464
#[tokio::test]
426465
async fn challenge_response_chunk_after_verifying_is_ack_and_ignore() {
427466
let mut state = StoredEvaluatorState::default();

0 commit comments

Comments
 (0)