Private/Shamir/Split-SecretSharingShamirSecret.ps1

function Split-SecretSharingShamirSecret {
    <#
    .SYNOPSIS
        Splits a byte-array secret into Shamir share points over GF(256).
    .DESCRIPTION
        Constructs a degree-(Threshold-1) polynomial that passes through the secret (at
        SecretXCoordinate) and any caller-supplied FixedPoint anchors (e.g. a SLIP-0039
        digest share), then returns one share point per requested XCoordinate: the first
        (Threshold - 1 - FixedPoint.Count) of them are literal cryptographically random
        byte arrays, and the remainder are derived from the random and fixed points via
        Lagrange interpolation. This is the same construction the SLIP-0039 reference
        implementation uses - it is equivalent to generating random polynomial
        coefficients directly, since a degree-(Threshold-1) polynomial is uniquely
        determined by any Threshold points on it.

        When Threshold is 1, every requested share is simply the secret itself, and no
        randomness or FixedPoint anchors are used or accepted (matching SLIP-0039's
        treatment of the threshold=1 case, where no digest share is needed either).
    #>

    [CmdletBinding()]
    [OutputType([PSObject[]], [System.Object[]])]
    param(
        [Parameter(Mandatory)]
        [byte[]]$Secret,

        [Parameter(Mandatory)]
        [byte]$SecretXCoordinate,

        [Parameter(Mandatory)]
        [ValidateRange(1, 255)]
        [int]$Threshold,

        [Parameter(Mandatory)]
        [byte[]]$XCoordinate,

        [PSObject[]]$FixedPoint = @()
    )

    $allXCoordinates = @($SecretXCoordinate) + @($FixedPoint | ForEach-Object { $_.X }) + $XCoordinate
    $uniqueCount = ($allXCoordinates | Select-Object -Unique | Measure-Object).Count
    if ($uniqueCount -ne $allXCoordinates.Count) {
        throw 'Invalid request. SecretXCoordinate, FixedPoint, and XCoordinate values must all be distinct.'
    }

    if ($Threshold -eq 1) {
        if ($FixedPoint.Count -gt 0) {
            throw 'A threshold of 1 does not use FixedPoint anchors.'
        }

        return , @($XCoordinate | ForEach-Object {
                [PSCustomObject]@{ X = $_; Value = $Secret }
            })
    }

    $randomCount = $Threshold - 1 - $FixedPoint.Count
    if ($randomCount -lt 0) {
        throw 'Threshold is too small for the number of FixedPoint anchors supplied.'
    }
    if ($randomCount -gt $XCoordinate.Count) {
        throw 'Not enough XCoordinate values requested to hold the random shares this threshold requires.'
    }

    $shares = New-Object System.Collections.Generic.List[PSObject]
    for ($i = 0; $i -lt $randomCount; $i++) {
        $shares.Add([PSCustomObject]@{
                X     = $XCoordinate[$i]
                Value = Get-SecretSharingRandomByte -Length $Secret.Length
            })
    }

    $basePoint = @($shares.ToArray()) + @($FixedPoint) + @([PSCustomObject]@{ X = $SecretXCoordinate; Value = $Secret })

    for ($i = $randomCount; $i -lt $XCoordinate.Count; $i++) {
        $shares.Add([PSCustomObject]@{
                X     = $XCoordinate[$i]
                Value = Invoke-SecretSharingShamirInterpolation -Point $basePoint -X $XCoordinate[$i]
            })
    }

    return , $shares.ToArray()
}