@@ -161,7 +161,7 @@ impl ShamirSecret {
161161 }
162162
163163 pub fn split ( secret : & [ u8 ] , part : u8 , threshold : u8 ) -> Result < Zeroizing < Vec < Vec < u8 > > > , RvError > {
164- if part < threshold || threshold < 2 {
164+ if part < threshold || threshold < 2 || part >= 255 {
165165 return Err ( RvError :: ErrShamirShareCountInvalid ) ;
166166 }
167167
@@ -390,4 +390,296 @@ mod tests {
390390 let secret = recovered. unwrap ( ) ;
391391 assert_ne ! ( & secret, "Hello World!" . as_bytes( ) ) ;
392392 }
393+
394+ #[ test]
395+ fn test_split_basic_functionality ( ) {
396+ let secret = b"test secret data" ;
397+ let threshold = 3 ;
398+ let total_shares = 5 ;
399+
400+ let result = ShamirSecret :: split ( secret, total_shares, threshold) ;
401+ assert ! ( result. is_ok( ) ) ;
402+
403+ let shares = result. unwrap ( ) ;
404+ println ! ( "shares: {:?}" , shares) ;
405+ assert_eq ! ( shares. len( ) , total_shares as usize ) ;
406+
407+ let expected_length = secret. len ( ) + 1 ; // secret length + 1 byte for share ID
408+ for share in shares. iter ( ) {
409+ assert_eq ! ( share. len( ) , expected_length) ;
410+ }
411+
412+ for ( i, share) in shares. iter ( ) . enumerate ( ) {
413+ assert_eq ! ( share[ share. len( ) - 1 ] , ( i + 1 ) as u8 ) ;
414+ }
415+ }
416+
417+ #[ test]
418+ fn test_split_parameter_validation ( ) {
419+ let secret = b"test secret" ;
420+
421+ // threshold < 2 should fail
422+ let result = ShamirSecret :: split ( secret, 3 , 1 ) ;
423+ assert ! ( result. is_err( ) ) ;
424+
425+ // part < threshold should fail
426+ let result = ShamirSecret :: split ( secret, 2 , 3 ) ;
427+ assert ! ( result. is_err( ) ) ;
428+
429+ // Valid parameters should succeed
430+ let result = ShamirSecret :: split ( secret, 3 , 2 ) ;
431+ assert ! ( result. is_ok( ) ) ;
432+
433+ let result = ShamirSecret :: split ( secret, 5 , 3 ) ;
434+ assert ! ( result. is_ok( ) ) ;
435+ }
436+
437+ #[ test]
438+ fn test_split_minimum_valid_parameters ( ) {
439+ let secret = b"minimum test" ;
440+ let threshold = 2 ;
441+ let total_shares = 2 ;
442+
443+ let result = ShamirSecret :: split ( secret, total_shares, threshold) ;
444+ assert ! ( result. is_ok( ) ) ;
445+
446+ let shares = result. unwrap ( ) ;
447+ assert_eq ! ( shares. len( ) , 2 ) ;
448+
449+ // Test that we can recover the secret with both shares
450+ let recovered = ShamirSecret :: combine ( shares. to_vec ( ) ) ;
451+ assert ! ( recovered. is_some( ) ) ;
452+ assert_eq ! ( recovered. unwrap( ) , secret) ;
453+ }
454+
455+ #[ test]
456+ fn test_split_maximum_shares ( ) {
457+ let secret = b"max shares test" ;
458+ let threshold = 2 ;
459+ let total_shares = 255 ; // Maximum possible share count
460+
461+ let result = ShamirSecret :: split ( secret, total_shares, threshold) ;
462+ assert ! ( result. is_err( ) ) ;
463+
464+ let result = ShamirSecret :: split ( secret, total_shares - 1 , threshold) ;
465+ assert ! ( result. is_ok( ) ) ;
466+
467+ let shares = result. unwrap ( ) ;
468+ assert_eq ! ( shares. len( ) , ( total_shares - 1 ) as usize ) ;
469+
470+ // Test recovery with minimum threshold
471+ let minimal_shares = vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) ] ;
472+ let recovered = ShamirSecret :: combine ( minimal_shares) ;
473+ assert ! ( recovered. is_some( ) ) ;
474+ assert_eq ! ( recovered. unwrap( ) , secret) ;
475+ }
476+
477+ #[ test]
478+ fn test_split_empty_secret ( ) {
479+ let secret = b"" ;
480+ let threshold = 2 ;
481+ let total_shares = 3 ;
482+
483+ let result = ShamirSecret :: split ( secret, total_shares, threshold) ;
484+ assert ! ( result. is_ok( ) ) ;
485+
486+ let shares = result. unwrap ( ) ;
487+ assert_eq ! ( shares. len( ) , 3 ) ;
488+
489+ // Each share should only contain the share ID
490+ for share in shares. iter ( ) {
491+ assert_eq ! ( share. len( ) , 1 ) ; // Only share ID
492+ }
493+
494+ // Recovery should work
495+ let recovered = ShamirSecret :: combine ( vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) ] ) ;
496+ assert ! ( recovered. is_some( ) ) ;
497+ assert_eq ! ( recovered. unwrap( ) , secret) ;
498+ }
499+
500+ #[ test]
501+ fn test_split_single_byte_secret ( ) {
502+ let secret = b"A" ;
503+ let threshold = 3 ;
504+ let total_shares = 5 ;
505+
506+ let result = ShamirSecret :: split ( secret, total_shares, threshold) ;
507+ assert ! ( result. is_ok( ) ) ;
508+
509+ let shares = result. unwrap ( ) ;
510+ assert_eq ! ( shares. len( ) , 5 ) ;
511+
512+ for share in shares. iter ( ) {
513+ assert_eq ! ( share. len( ) , 2 ) ; // 1 byte secret + 1 byte share ID
514+ }
515+
516+ // Recovery with exact threshold
517+ let recovered = ShamirSecret :: combine ( vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) , shares[ 2 ] . clone( ) ] ) ;
518+ assert ! ( recovered. is_some( ) ) ;
519+ assert_eq ! ( recovered. unwrap( ) , secret) ;
520+ }
521+
522+ #[ test]
523+ fn test_split_large_secret ( ) {
524+ let secret = vec ! [ 0xAB ; 1000 ] ; // 1KB of data
525+ let threshold = 4 ;
526+ let total_shares = 7 ;
527+
528+ let result = ShamirSecret :: split ( & secret, total_shares, threshold) ;
529+ assert ! ( result. is_ok( ) ) ;
530+
531+ let shares = result. unwrap ( ) ;
532+ assert_eq ! ( shares. len( ) , 7 ) ;
533+
534+ for share in shares. iter ( ) {
535+ assert_eq ! ( share. len( ) , 1001 ) ; // 1000 bytes secret + 1 byte share ID
536+ }
537+
538+ // Recovery with more than threshold
539+ let recovered = ShamirSecret :: combine ( vec ! [
540+ shares[ 0 ] . clone( ) ,
541+ shares[ 1 ] . clone( ) ,
542+ shares[ 2 ] . clone( ) ,
543+ shares[ 3 ] . clone( ) ,
544+ shares[ 4 ] . clone( ) ,
545+ ] ) ;
546+ assert ! ( recovered. is_some( ) ) ;
547+ assert_eq ! ( recovered. unwrap( ) , secret) ;
548+ }
549+
550+ #[ test]
551+ fn test_split_recovery_with_exact_threshold ( ) {
552+ let secret = b"exact threshold test" ;
553+ let threshold = 4 ;
554+ let total_shares = 6 ;
555+
556+ let shares = ShamirSecret :: split ( secret, total_shares, threshold) . unwrap ( ) ;
557+
558+ // Test recovery with exactly threshold shares
559+ let recovered =
560+ ShamirSecret :: combine ( vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) , shares[ 2 ] . clone( ) , shares[ 3 ] . clone( ) ] ) ;
561+ assert ! ( recovered. is_some( ) ) ;
562+ assert_eq ! ( recovered. unwrap( ) , secret) ;
563+ }
564+
565+ #[ test]
566+ fn test_split_recovery_with_insufficient_shares ( ) {
567+ let secret = b"insufficient shares test" ;
568+ let threshold = 4 ;
569+ let total_shares = 6 ;
570+
571+ let shares = ShamirSecret :: split ( secret, total_shares, threshold) . unwrap ( ) ;
572+
573+ // Test recovery with less than threshold shares (should fail or give wrong result)
574+ let recovered = ShamirSecret :: combine ( vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) , shares[ 2 ] . clone( ) ] ) ;
575+
576+ // With insufficient shares, either recovery fails or gives wrong result
577+ if let Some ( recovered_secret) = recovered {
578+ assert_ne ! ( recovered_secret, secret) ;
579+ }
580+ }
581+
582+ #[ test]
583+ fn test_split_recovery_with_different_share_combinations ( ) {
584+ let secret = b"combination test" ;
585+ let threshold = 3 ;
586+ let total_shares = 5 ;
587+
588+ let shares = ShamirSecret :: split ( secret, total_shares, threshold) . unwrap ( ) ;
589+
590+ // Test different combinations of shares
591+ let combinations = vec ! [
592+ vec![ 0 , 1 , 2 ] ,
593+ vec![ 0 , 2 , 4 ] ,
594+ vec![ 1 , 3 , 4 ] ,
595+ vec![ 2 , 3 , 4 ] ,
596+ vec![ 0 , 1 , 3 , 4 ] , // More than threshold
597+ ] ;
598+
599+ for combination in combinations {
600+ let selected_shares: Vec < _ > = combination. iter ( ) . map ( |& i| shares[ i] . clone ( ) ) . collect ( ) ;
601+ let recovered = ShamirSecret :: combine ( selected_shares) ;
602+ assert ! ( recovered. is_some( ) ) ;
603+ assert_eq ! ( recovered. unwrap( ) , secret) ;
604+ }
605+ }
606+
607+ #[ test]
608+ fn test_split_deterministic_behavior ( ) {
609+ // Note: ShamirSecret uses random coefficients, so splits are not deterministic
610+ // But we can test that the same secret with same parameters produces valid shares
611+ let secret = b"deterministic test" ;
612+ let threshold = 3 ;
613+ let total_shares = 5 ;
614+
615+ let shares1 = ShamirSecret :: split ( secret, total_shares, threshold) . unwrap ( ) ;
616+ let shares2 = ShamirSecret :: split ( secret, total_shares, threshold) . unwrap ( ) ;
617+
618+ // Shares should be different due to randomness
619+ assert_ne ! ( shares1[ 0 ] , shares2[ 0 ] ) ;
620+
621+ // But both should recover to the same secret
622+ let recovered1 = ShamirSecret :: combine ( vec ! [ shares1[ 0 ] . clone( ) , shares1[ 1 ] . clone( ) , shares1[ 2 ] . clone( ) ] ) ;
623+ let recovered2 = ShamirSecret :: combine ( vec ! [ shares2[ 0 ] . clone( ) , shares2[ 1 ] . clone( ) , shares2[ 2 ] . clone( ) ] ) ;
624+
625+ assert ! ( recovered1. is_some( ) ) ;
626+ assert ! ( recovered2. is_some( ) ) ;
627+ assert_eq ! ( recovered1. unwrap( ) , secret) ;
628+ assert_eq ! ( recovered2. unwrap( ) , secret) ;
629+ }
630+
631+ #[ test]
632+ fn test_split_binary_data ( ) {
633+ // Test with binary data including null bytes and high values
634+ let secret = vec ! [ 0x00 , 0xFF , 0x80 , 0x7F , 0x01 , 0xFE , 0x55 , 0xAA ] ;
635+ let threshold = 3 ;
636+ let total_shares = 5 ;
637+
638+ let shares = ShamirSecret :: split ( & secret, total_shares, threshold) . unwrap ( ) ;
639+
640+ let recovered = ShamirSecret :: combine ( vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) , shares[ 2 ] . clone( ) ] ) ;
641+ assert ! ( recovered. is_some( ) ) ;
642+ assert_eq ! ( recovered. unwrap( ) , secret) ;
643+ }
644+
645+ #[ test]
646+ fn test_split_edge_case_thresholds ( ) {
647+ let secret = b"edge case test" ;
648+
649+ // Test with threshold = total_shares
650+ let shares = ShamirSecret :: split ( secret, 3 , 3 ) . unwrap ( ) ;
651+ assert_eq ! ( shares. len( ) , 3 ) ;
652+
653+ // Need all shares to recover
654+ let recovered = ShamirSecret :: combine ( vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) , shares[ 2 ] . clone( ) ] ) ;
655+ assert ! ( recovered. is_some( ) ) ;
656+ assert_eq ! ( recovered. unwrap( ) , secret) ;
657+
658+ // With one less share, recovery should fail or give wrong result
659+ let recovered = ShamirSecret :: combine ( vec ! [ shares[ 0 ] . clone( ) , shares[ 1 ] . clone( ) ] ) ;
660+ if let Some ( recovered_secret) = recovered {
661+ assert_ne ! ( recovered_secret, secret) ;
662+ }
663+ }
664+
665+ #[ test]
666+ fn test_split_share_uniqueness ( ) {
667+ let secret = b"uniqueness test" ;
668+ let threshold = 3 ;
669+ let total_shares = 5 ;
670+
671+ let shares = ShamirSecret :: split ( secret, total_shares, threshold) . unwrap ( ) ;
672+
673+ // All shares should be unique
674+ for i in 0 ..shares. len ( ) {
675+ for j in ( i + 1 ) ..shares. len ( ) {
676+ assert_ne ! ( shares[ i] , shares[ j] ) ;
677+ }
678+ }
679+
680+ // Share IDs should be unique and in order
681+ for ( i, share) in shares. iter ( ) . enumerate ( ) {
682+ assert_eq ! ( share[ share. len( ) - 1 ] , ( i + 1 ) as u8 ) ;
683+ }
684+ }
393685}
0 commit comments