@@ -30,8 +30,10 @@ actor \nodoc\ Main is TestList
3030 test (Property1UnitTest [USize ](_TestPbkdf2Sha256Deterministic ))
3131 end
3232 ifdef " openssl_3.0.x" or " openssl_4.0.x" then
33- test (Property1UnitTest [USize ](_TestShake128OutputLength ))
34- test (Property1UnitTest [USize ](_TestShake256OutputLength ))
33+ test (_TestShake128KnownAnswer )
34+ test (_TestShake256KnownAnswer )
35+ test (Property1UnitTest [USize ](_TestShake128XofPrefixSmall ))
36+ test (Property1UnitTest [USize ](_TestShake256XofPrefixSmall ))
3537 test (Property1UnitTest [USize ](_TestShake128XofPrefix ))
3638 test (Property1UnitTest [USize ](_TestShake256XofPrefix ))
3739 end
@@ -446,37 +448,103 @@ class \nodoc\ iso _TestRandBytesNonConstant is Property1[USize]
446448 let b = RandBytes (sample)?
447449 h.assert_false (ConstantTimeCompare (a, b))
448450
449- class \nodoc\ iso _TestShake128OutputLength is Property1 [USize ]
450- fun name (): String => " crypto/Shake128/property/output_length"
451+ class \nodoc\ iso _TestShake128KnownAnswer is UnitTest
452+ """
453+ Known-answer tests for SHAKE128 at two non-default output lengths.
454+ Anchors the XOF path against specific byte values so a silent no-op
455+ (zero bytes written) cannot pass.
456+ """
457+ fun name (): String => " crypto/Shake128/known_answer"
451458
452- fun gen (): Generator [USize ] =>
453- Generators .usize (1 , 256 )
459+ fun apply (h: TestHelper ) ? =>
460+ ifdef " openssl_3.0.x" or " openssl_4.0.x" then
461+ let d32 = Digest .shake128 (32 )
462+ d32.append (" message1" )?
463+ d32.append (" message2" )?
464+ h.assert_eq [String ](
465+ " 0d11671f23b6356bdf4ba8dcae37419df1d0875e1a15c7859eb3ba0096aa262f" ,
466+ ToHexString (d32.final ()))
454467
455- fun ref property (sample: USize , h: PropertyHelper ) ? =>
468+ let d64 = Digest .shake128 (64 )
469+ d64.append (" message1" )?
470+ d64.append (" message2" )?
471+ h.assert_eq [String ](
472+ " 0d11671f23b6356bdf4ba8dcae37419df1d0875e1a15c7859eb3ba0096aa262f" +
473+ " 3a1cfc86db5b324ac3f8220645ec0740c2171a0b935f362d0c3bfa5ab51be5d0" ,
474+ ToHexString (d64.final ()))
475+ end
476+
477+ class \nodoc\ iso _TestShake256KnownAnswer is UnitTest
478+ """
479+ Known-answer tests for SHAKE256 at two non-default output lengths.
480+ Anchors the XOF path against specific byte values so a silent no-op
481+ (zero bytes written) cannot pass.
482+ """
483+ fun name (): String => " crypto/Shake256/known_answer"
484+
485+ fun apply (h: TestHelper ) ? =>
456486 ifdef " openssl_3.0.x" or " openssl_4.0.x" then
457- let d = Digest .shake128 (sample)
458- d.append (" test" )?
459- h.assert_eq [USize ](sample, d.final ().size ())
487+ let d64 = Digest .shake256 (64 )
488+ d64.append (" message1" )?
489+ d64.append (" message2" )?
490+ h.assert_eq [String ](
491+ " 80e2bbb14639e3b1fc1df80b47b67fb518b0ed26a1caddfa10d68f7992c33820" +
492+ " 2d0b17a5ebbcef93f51247497f60bcd3f2809a967874d017ef5b51d6b08836cc" ,
493+ ToHexString (d64.final ()))
494+
495+ let d128 = Digest .shake256 (128 )
496+ d128.append (" message1" )?
497+ d128.append (" message2" )?
498+ h.assert_eq [String ](
499+ " 80e2bbb14639e3b1fc1df80b47b67fb518b0ed26a1caddfa10d68f7992c33820" +
500+ " 2d0b17a5ebbcef93f51247497f60bcd3f2809a967874d017ef5b51d6b08836cc" +
501+ " af79f3db3fafdf89e7d42270472c3d1a8e55c52a30859e01b5fceba359c21c1e" +
502+ " 76b73180378604d46061c87e65c4740c8ff9721ed16465cef66fefc3c6f2070c" ,
503+ ToHexString (d128.final ()))
460504 end
461505
462- class \nodoc\ iso _TestShake256OutputLength is Property1 [USize ]
463- fun name (): String => " crypto/Shake256/property/output_length"
506+ class \nodoc\ iso _TestShake128XofPrefixSmall is Property1 [USize ]
507+ """
508+ SHAKE128 prefix property at small output sizes (2..15 bytes). Exercises
509+ the truncation path where off-by-one partial-block bugs typically live.
510+ No KAT anchor — sizes below 16 are guarded by _TestShake128XofPrefix
511+ at larger sizes.
512+ """
513+ fun name (): String => " crypto/Shake128/property/xof_prefix_small"
464514
465515 fun gen (): Generator [USize ] =>
466- Generators .usize (1 , 256 )
516+ Generators .usize (2 , 15 )
467517
468518 fun ref property (sample: USize , h: PropertyHelper ) ? =>
469519 ifdef " openssl_3.0.x" or " openssl_4.0.x" then
470- let d = Digest .shake256 (sample)
471- d.append (" test" )?
472- h.assert_eq [USize ](sample, d.final ().size ())
520+ let small_size = sample / 2
521+ let large_size = sample
522+
523+ let small = Digest .shake128 (small_size)
524+ small.append (" test input" )?
525+ let small_result = small.final ()
526+
527+ let large = Digest .shake128 (large_size)
528+ large.append (" test input" )?
529+ let large_result = large.final ()
530+
531+ h.assert_array_eq [U8 ](small_result,
532+ large_result.trim (0 , small_size))
473533 end
474534
475535class \nodoc\ iso _TestShake128XofPrefix is Property1 [USize ]
536+ """
537+ SHAKE128 prefix property: the first N bytes of output at length M (M > N)
538+ are identical to the full output at length N. A KAT anchor at the full
539+ length prevents any no-op or incorrect XOF implementation from satisfying
540+ the prefix equality (an all-zero buffer would trivially equal its own
541+ prefix).
542+ """
476543 fun name (): String => " crypto/Shake128/property/xof_prefix"
477544
478545 fun gen (): Generator [USize ] =>
479- Generators .usize (2 , 256 )
546+ // Minimum 16 so the 16-byte KAT anchor below always applies.
547+ Generators .usize (16 , 256 )
480548
481549 fun ref property (sample: USize , h: PropertyHelper ) ? =>
482550 ifdef " openssl_3.0.x" or " openssl_4.0.x" then
@@ -491,15 +559,57 @@ class \nodoc\ iso _TestShake128XofPrefix is Property1[USize]
491559 large.append (" test input" )?
492560 let large_result = large.final ()
493561
562+ h.assert_array_eq [U8 ](small_result,
563+ large_result.trim (0 , small_size))
564+
565+ // KAT anchor: first 16 bytes of SHAKE128("test input").
566+ h.assert_eq [String ](
567+ " a9d2b0362d0e2e961eeb969ce9a42f2d" ,
568+ ToHexString (large_result.trim (0 , 16 )))
569+ end
570+
571+ class \nodoc\ iso _TestShake256XofPrefixSmall is Property1 [USize ]
572+ """
573+ SHAKE256 prefix property at small output sizes (2..31 bytes). Exercises
574+ the truncation path where off-by-one partial-block bugs typically live.
575+ No KAT anchor — sizes below 32 are guarded by _TestShake256XofPrefix
576+ at larger sizes.
577+ """
578+ fun name (): String => " crypto/Shake256/property/xof_prefix_small"
579+
580+ fun gen (): Generator [USize ] =>
581+ Generators .usize (2 , 31 )
582+
583+ fun ref property (sample: USize , h: PropertyHelper ) ? =>
584+ ifdef " openssl_3.0.x" or " openssl_4.0.x" then
585+ let small_size = sample / 2
586+ let large_size = sample
587+
588+ let small = Digest .shake256 (small_size)
589+ small.append (" test input" )?
590+ let small_result = small.final ()
591+
592+ let large = Digest .shake256 (large_size)
593+ large.append (" test input" )?
594+ let large_result = large.final ()
595+
494596 h.assert_array_eq [U8 ](small_result,
495597 large_result.trim (0 , small_size))
496598 end
497599
498600class \nodoc\ iso _TestShake256XofPrefix is Property1 [USize ]
601+ """
602+ SHAKE256 prefix property: the first N bytes of output at length M (M > N)
603+ are identical to the full output at length N. A KAT anchor at the full
604+ length prevents any no-op or incorrect XOF implementation from satisfying
605+ the prefix equality (an all-zero buffer would trivially equal its own
606+ prefix).
607+ """
499608 fun name (): String => " crypto/Shake256/property/xof_prefix"
500609
501610 fun gen (): Generator [USize ] =>
502- Generators .usize (2 , 256 )
611+ // Minimum 32 so the 32-byte KAT anchor below always applies.
612+ Generators .usize (32 , 256 )
503613
504614 fun ref property (sample: USize , h: PropertyHelper ) ? =>
505615 ifdef " openssl_3.0.x" or " openssl_4.0.x" then
@@ -516,6 +626,11 @@ class \nodoc\ iso _TestShake256XofPrefix is Property1[USize]
516626
517627 h.assert_array_eq [U8 ](small_result,
518628 large_result.trim (0 , small_size))
629+
630+ // KAT anchor: first 32 bytes of SHAKE256("test input").
631+ h.assert_eq [String ](
632+ " e952d90136cb23413ff22b266e2f5dd42294a34bc311394b04863c039011f179" ,
633+ ToHexString (large_result.trim (0 , 32 )))
519634 end
520635
521636class \nodoc\ iso _TestHashFnOutputLength is Property1 [USize ]
0 commit comments