Private/Share/ConvertFrom-SecretSharingShareWord.ps1

function ConvertFrom-SecretSharingShareWord {
    <#
    .SYNOPSIS
        Unpacks a SLIP-0039 share's header fields and value from its data words.
    .DESCRIPTION
        The inverse of ConvertTo-SecretSharingShareWord. Index must be the share's
        data words only - the header and value, with the trailing 3-word checksum
        already stripped and verified by the caller (Test-SecretSharingChecksum),
        exactly mirroring the boundary ConvertTo-SecretSharingShareWord produces.

        The number of padding bits in the first value word is derived from Index's
        length rather than passed in, the same way the reference implementation
        derives it from the mnemonic's total word count. A value that doesn't fit in
        the resulting byte count - which only happens if the padding bits were not
        all zero, i.e. a corrupted or invalid share - raises an error rather than
        silently truncating.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [int[]]$Index
    )

    $minimumValueWordCount = [int][System.Math]::Ceiling(128 / 10.0)
    if ($Index.Count -lt (4 + $minimumValueWordCount)) {
        throw "Invalid share data. Expected at least $(4 + $minimumValueWordCount) words, got $($Index.Count)."
    }

    $paddingBitLength = (10 * ($Index.Count - 4)) % 16
    if ($paddingBitLength -gt 8) {
        throw 'Invalid share data length.'
    }

    $idExpInt = ConvertFrom-SecretSharingWordIndex -Index $Index[0..1] -RadixBit 10
    $iterationExponent = [int]($idExpInt % 16)
    $remaining = [System.Numerics.BigInteger]::Divide($idExpInt, 16)
    $extendableBit = [int]($remaining % 2)
    $identifier = [int]([System.Numerics.BigInteger]::Divide($remaining, 2))

    $paramInt = ConvertFrom-SecretSharingWordIndex -Index $Index[2..3] -RadixBit 10
    $memberThreshold = [int]($paramInt % 16) + 1
    $remaining = [System.Numerics.BigInteger]::Divide($paramInt, 16)
    $memberIndex = [int]($remaining % 16)
    $remaining = [System.Numerics.BigInteger]::Divide($remaining, 16)
    $groupCount = [int]($remaining % 16) + 1
    $remaining = [System.Numerics.BigInteger]::Divide($remaining, 16)
    $groupThreshold = [int]($remaining % 16) + 1
    $remaining = [System.Numerics.BigInteger]::Divide($remaining, 16)
    $groupIndex = [int]$remaining

    if ($groupCount -lt $groupThreshold) {
        throw 'Invalid share data. Group threshold cannot be greater than group count.'
    }

    $valueWord = $Index[4..($Index.Count - 1)]
    $valueBitLength = (10 * $valueWord.Count) - $paddingBitLength
    $valueByteCount = [int][System.Math]::Ceiling($valueBitLength / 8.0)
    $valueInt = ConvertFrom-SecretSharingWordIndex -Index $valueWord -RadixBit 10
    $value = ConvertFrom-SecretSharingBigIntegerToByte -Value $valueInt -Length $valueByteCount

    return [PSCustomObject]@{
        Identifier        = $identifier
        Extendable        = [bool]$extendableBit
        IterationExponent = $iterationExponent
        GroupIndex        = $groupIndex
        GroupThreshold    = $groupThreshold
        GroupCount        = $groupCount
        MemberIndex       = $memberIndex
        MemberThreshold   = $memberThreshold
        Value             = $value
    }
}