Public/Resolve-ShamirSecretShares.ps1

<#
.SYNOPSIS
    Recovers a secret from Shamir's Secret Sharing scheme shares.
 
.DESCRIPTION
    This function takes an array of Base64 encoded share objects and recovers the original secret string using Shamir's Secret Sharing. It deserializes the shares, extracts individual share information, sorts shares by chunk index, recovers each chunk secret, and finally combines the recovered chunks back into the original secret.
 
.PARAMETER Shares
    An array of strings containing Base64 encoded share objects, generated by the `Get-ShamirSecretShares` function.
 
.OUTPUTS
    The recovered secret as a byte array. You can further convert it to a string using `ConvertFrom-Bytes`.
 
.EXAMPLE
    Recover a secret from shares:
    Resolve-ShamirSecretShares -Shares $Shares
#>

Function Resolve-ShamirSecretShares {
    Param(
        [Parameter(Mandatory)]
        [String[]]$Shares
    )

    # Retrieve the actual encrypted data from the base64 string
    Try {
        $UnserializedData = $Shares | ConvertFrom-SerializedObject
    } Catch {
        Throw [System.Management.Automation.ParsingMetadataException] "Could not unserialize share data"
    }


    # Unzip the shares
    $AllShares = Foreach($SharePackageById in $UnserializedData) {
        $ShareId = $SharePackageById.Id

        Foreach($Share in $SharePackageById.Data) {
            [PSCustomObject]@{
                id = $ShareId
                value = $Share.value
                chunkindex = $Share.chunkindex
            }
        }
    }

    $SortedChunks = $UnserializedData.Data.chunkindex | Select-Object -Unique | Sort-Object
    $RecoveredChunks = Foreach($ChunkIndex in $SortedChunks) {
        # Get all shares for the current data chunk
        $ChunkShares = $AllShares | Where-Object chunkindex -eq $ChunkIndex

        $ExpectedShares = $ChunkShares | Select-Object @{
            Name = 'id'
            Expr = { $_.id }
        }, @{
            Name = 'value'
            Expr = { $_.value }
        }

        [String]$Secret = Resolve-Secret $ExpectedShares

        # Pad the resolved secret to accomodate for lost leading zeros due to the type conversion
        If(($Secret.Length % 3) -ne 0) {
            $ExpectedSize = $Secret.Length + (3 - ($Secret.Length % 3))
            $Secret = $Secret.PadLeft($ExpectedSize, '0')
        }

        Write-Output $Secret
    }

    # Join the resolved secrets and split the resulting string to chunks of 3 chars
    Try {
        # TODO: Find a better way to handle supposedly wrong or few shares
        [Byte[]]$Bytes = Split-Array -Array ($RecoveredChunks -join '').ToCharArray() -ChunkSize 3 | Foreach-Object { $_ -join '' }
    } Catch {
        throw "Could not resolve secret. Did you provide the correct amount of the correct shares?"
    }

    $Bytes | ConvertFrom-Bytes
}