Tests/Private/Share.Tests.ps1

BeforeDiscovery {
    Import-Module (Join-Path $PSScriptRoot '../../Posh-SecretSharing.psd1') -Force
}

Describe 'Private/Share' -Tag Unit {
    InModuleScope 'Posh-SecretSharing' {
        Describe 'ConvertTo-SecretSharingBigIntegerFromByte / ConvertFrom-SecretSharingBigIntegerToByte' {
            It 'round-trips an arbitrary byte array' {
                $bytes = [byte[]](0x01, 0x02, 0x03)
                $bi = ConvertTo-SecretSharingBigIntegerFromByte -Byte $bytes
                $bi | Should -Be 66051
                $back = ConvertFrom-SecretSharingBigIntegerToByte -Value $bi -Length 3
                $back | Should -Be $bytes
            }

            It 'treats the most-significant input byte as unsigned even when its high bit is set' {
                $bytes = [byte[]](0xFF, 0x00, 0x00, 0x00)
                $bi = ConvertTo-SecretSharingBigIntegerFromByte -Byte $bytes
                $bi | Should -BeGreaterThan 0
                $bi | Should -Be 4278190080
                $back = ConvertFrom-SecretSharingBigIntegerToByte -Value $bi -Length 4
                $back | Should -Be $bytes
            }

            It 'round-trips a full 32-byte (256-bit) value' {
                $bytes = [byte[]](1..32 | ForEach-Object { 0xFF })
                $bi = ConvertTo-SecretSharingBigIntegerFromByte -Byte $bytes
                $back = ConvertFrom-SecretSharingBigIntegerToByte -Value $bi -Length 32
                $back | Should -Be $bytes
            }

            It 'zero-pads a value smaller than the requested length' {
                $back = ConvertFrom-SecretSharingBigIntegerToByte -Value ([System.Numerics.BigInteger]5) -Length 4
                $back | Should -Be ([byte[]](0x00, 0x00, 0x00, 0x05))
            }

            It 'throws when the value does not fit in the requested length' {
                $tooBig = [System.Numerics.BigInteger]::Parse('999999999999999999999999999999999999999999')
                { ConvertFrom-SecretSharingBigIntegerToByte -Value $tooBig -Length 4 } | Should -Throw
            }

            It 'treats an empty byte array as zero' {
                (ConvertTo-SecretSharingBigIntegerFromByte -Byte ([byte[]]@())) | Should -Be 0
            }
        }

        Describe 'ConvertTo-SecretSharingWordIndex / ConvertFrom-SecretSharingWordIndex' {
            It 'splits a known integer into the expected base-1024 digits' {
                $digit = ConvertTo-SecretSharingWordIndex -Value 1234567 -Length 3 -RadixBit 10
                $digit.Count | Should -Be 3
                $expected = [System.Numerics.BigInteger]0
                foreach ($d in $digit) { $expected = ($expected * 1024) + $d }
                $expected | Should -Be 1234567
            }

            It 'round-trips through ConvertFrom' {
                $digit = ConvertTo-SecretSharingWordIndex -Value 987654 -Length 3 -RadixBit 10
                (ConvertFrom-SecretSharingWordIndex -Index $digit -RadixBit 10) | Should -Be 987654
            }

            It 'works with a 4-bit radix (share-parameter fields)' {
                $digit = ConvertTo-SecretSharingWordIndex -Value 0xABCDE -Length 5 -RadixBit 4
                $digit | Should -Be @(0xA, 0xB, 0xC, 0xD, 0xE)
            }

            It 'left-pads with zero digits when Length exceeds what the value needs' {
                $digit = ConvertTo-SecretSharingWordIndex -Value 5 -Length 4 -RadixBit 10
                $digit | Should -Be @(0, 0, 0, 5)
            }
        }

        Describe 'ConvertTo-SecretSharingShareWord / ConvertFrom-SecretSharingShareWord' {
            It 'round-trips a full set of header fields and a 16-byte value' {
                $value = 1..16 | ForEach-Object { [byte]$_ }
                $words = ConvertTo-SecretSharingShareWord -Identifier 12345 -IterationExponent 3 `
                    -GroupIndex 2 -GroupThreshold 3 -GroupCount 5 -MemberIndex 1 -MemberThreshold 2 -Value $value

                $decoded = ConvertFrom-SecretSharingShareWord -Index $words

                $decoded.Identifier | Should -Be 12345
                $decoded.Extendable | Should -BeFalse
                $decoded.IterationExponent | Should -Be 3
                $decoded.GroupIndex | Should -Be 2
                $decoded.GroupThreshold | Should -Be 3
                $decoded.GroupCount | Should -Be 5
                $decoded.MemberIndex | Should -Be 1
                $decoded.MemberThreshold | Should -Be 2
                $decoded.Value | Should -Be $value
            }

            It 'round-trips the Extendable flag' {
                $value = 1..16 | ForEach-Object { [byte]$_ }
                $words = ConvertTo-SecretSharingShareWord -Identifier 1 -Extendable -IterationExponent 0 `
                    -GroupIndex 0 -GroupThreshold 1 -GroupCount 1 -MemberIndex 0 -MemberThreshold 1 -Value $value
                (ConvertFrom-SecretSharingShareWord -Index $words).Extendable | Should -BeTrue
            }

            It 'round-trips boundary field values (Identifier 0 and 32767, all 4-bit fields at their max)' {
                $value = 1..32 | ForEach-Object { [byte](256 - $_) }
                $words = ConvertTo-SecretSharingShareWord -Identifier 32767 -IterationExponent 15 `
                    -GroupIndex 15 -GroupThreshold 16 -GroupCount 16 -MemberIndex 15 -MemberThreshold 16 -Value $value
                $decoded = ConvertFrom-SecretSharingShareWord -Index $words

                $decoded.Identifier | Should -Be 32767
                $decoded.IterationExponent | Should -Be 15
                $decoded.GroupIndex | Should -Be 15
                $decoded.GroupThreshold | Should -Be 16
                $decoded.GroupCount | Should -Be 16
                $decoded.MemberIndex | Should -Be 15
                $decoded.MemberThreshold | Should -Be 16
                $decoded.Value | Should -Be $value

                $words0 = ConvertTo-SecretSharingShareWord -Identifier 0 -IterationExponent 0 `
                    -GroupIndex 0 -GroupThreshold 1 -GroupCount 1 -MemberIndex 0 -MemberThreshold 1 -Value $value
                (ConvertFrom-SecretSharingShareWord -Index $words0).Identifier | Should -Be 0
            }

            It 'round-trips a 32-byte (256-bit) value' {
                $value = 1..32 | ForEach-Object { [byte]$_ }
                $words = ConvertTo-SecretSharingShareWord -Identifier 500 -IterationExponent 0 `
                    -GroupIndex 0 -GroupThreshold 1 -GroupCount 1 -MemberIndex 0 -MemberThreshold 1 -Value $value
                (ConvertFrom-SecretSharingShareWord -Index $words).Value | Should -Be $value
            }

            It 'throws when GroupCount is less than GroupThreshold' {
                # ConvertTo-SecretSharingShareWord does not cross-validate Threshold vs Count (only
                # ConvertFrom does, mirroring the reference implementation's own decode-side check),
                # so encoding GroupThreshold > GroupCount succeeds and lets us exercise that check.
                $value = 1..16 | ForEach-Object { [byte]$_ }
                $words = ConvertTo-SecretSharingShareWord -Identifier 1 -IterationExponent 0 `
                    -GroupIndex 0 -GroupThreshold 5 -GroupCount 3 -MemberIndex 0 -MemberThreshold 1 -Value $value
                { ConvertFrom-SecretSharingShareWord -Index $words } | Should -Throw
            }

            It 'throws when given fewer than the minimum number of words' {
                { ConvertFrom-SecretSharingShareWord -Index ([int[]](1, 2, 3)) } | Should -Throw
            }
        }
    }
}