Skip to content

Commit a64598d

Browse files
test: add PCZT parse error path tests for hybrid-kem
Test MissingPqCiphertext and MissingDiversifierHint error variants in Output::parse when ct_pq or diversifier_hint are None. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b2ed15c commit a64598d

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

src/pczt/parse.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,116 @@ impl fmt::Display for ParseError {
406406

407407
#[cfg(feature = "std")]
408408
impl std::error::Error for ParseError {}
409+
410+
#[cfg(all(test, feature = "hybrid-kem"))]
411+
mod tests {
412+
use super::*;
413+
use crate::memo::ZcashMemo;
414+
415+
/// Helper: constructs minimal valid arguments for `Output::parse` (hybrid-kem mode).
416+
/// The cmx, enc_ciphertext, and out_ciphertext are syntactically valid but semantically
417+
/// meaningless — sufficient to reach the PQ field validation.
418+
fn valid_output_args() -> (
419+
Nullifier,
420+
[u8; 32],
421+
[u8; 32],
422+
Vec<u8>,
423+
Vec<u8>,
424+
[u8; 1088],
425+
[u8; 11],
426+
) {
427+
use rand::rngs::OsRng;
428+
let nf = Nullifier::dummy(&mut OsRng);
429+
// Zero is a valid Pallas base field element
430+
let cmx_bytes = [0u8; 32];
431+
let ephemeral_key = [0u8; 32];
432+
let enc_ciphertext = vec![0u8; 580]; // ZcashMemo NoteCiphertextBytes size
433+
let out_ciphertext = vec![0u8; 112]; // hybrid out_ciphertext size
434+
let ct_pq = [0u8; 1088];
435+
let diversifier_hint = [0u8; 11];
436+
(
437+
nf,
438+
cmx_bytes,
439+
ephemeral_key,
440+
enc_ciphertext,
441+
out_ciphertext,
442+
ct_pq,
443+
diversifier_hint,
444+
)
445+
}
446+
447+
#[test]
448+
fn output_parse_missing_pq_ciphertext() {
449+
let (nf, cmx, epk, enc, out, _ct_pq, div_hint) = valid_output_args();
450+
let result = Output::<ZcashMemo>::parse(
451+
nf,
452+
cmx,
453+
epk,
454+
enc,
455+
out,
456+
None, // ct_pq missing
457+
Some(div_hint),
458+
None,
459+
None,
460+
None,
461+
None,
462+
None,
463+
None,
464+
BTreeMap::new(),
465+
);
466+
assert!(
467+
matches!(result, Err(ParseError::MissingPqCiphertext)),
468+
"parse must fail with MissingPqCiphertext when ct_pq is None"
469+
);
470+
}
471+
472+
#[test]
473+
fn output_parse_missing_diversifier_hint() {
474+
let (nf, cmx, epk, enc, out, ct_pq, _div_hint) = valid_output_args();
475+
let result = Output::<ZcashMemo>::parse(
476+
nf,
477+
cmx,
478+
epk,
479+
enc,
480+
out,
481+
Some(ct_pq),
482+
None, // diversifier_hint missing
483+
None,
484+
None,
485+
None,
486+
None,
487+
None,
488+
None,
489+
BTreeMap::new(),
490+
);
491+
assert!(
492+
matches!(result, Err(ParseError::MissingDiversifierHint)),
493+
"parse must fail with MissingDiversifierHint when diversifier_hint is None"
494+
);
495+
}
496+
497+
#[test]
498+
fn output_parse_succeeds_with_all_pq_fields() {
499+
let (nf, cmx, epk, enc, out, ct_pq, div_hint) = valid_output_args();
500+
let result = Output::<ZcashMemo>::parse(
501+
nf,
502+
cmx,
503+
epk,
504+
enc,
505+
out,
506+
Some(ct_pq),
507+
Some(div_hint),
508+
None,
509+
None,
510+
None,
511+
None,
512+
None,
513+
None,
514+
BTreeMap::new(),
515+
);
516+
assert!(
517+
result.is_ok(),
518+
"parse must succeed when all PQ fields are provided"
519+
);
520+
}
521+
}

0 commit comments

Comments
 (0)