Private/Crypto/Unprotect-SecretSharingMasterSecret.ps1

function Unprotect-SecretSharingMasterSecret {
    <#
    .SYNOPSIS
        Decrypts a master secret encrypted by Protect-SecretSharingMasterSecret.
    .DESCRIPTION
        Runs the same 4-round Feistel network as Protect-SecretSharingMasterSecret, but
        in reverse round order, which undoes the encryption exactly when Passphrase,
        Identifier, IterationExponent, and Extendable all match what was used to encrypt.
        There is no way to detect a wrong Passphrase from this function alone - it always
        returns some value of the right length, correct or not; a caller that needs to
        know whether decryption actually succeeded must check the result some other way
        (e.g. Test-SecretSharingDigestShare, once the reconstructed secret's shares have
        been recombined).
    #>

    [CmdletBinding()]
    [OutputType([byte[]], [System.Object[]])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Pure in-memory computation; no external state is changed.')]
    param(
        [Parameter(Mandatory)]
        [byte[]]$EncryptedMasterSecret,

        [byte[]]$Passphrase = @(),

        [Parameter(Mandatory)]
        [ValidateRange(0, 32767)]
        [int]$Identifier,

        [Parameter(Mandatory)]
        [ValidateRange(0, 15)]
        [int]$IterationExponent,

        [switch]$Extendable
    )

    $constant = Get-SecretSharingCipherConstant
    if ($EncryptedMasterSecret.Length -lt $constant.MinimumSecretLength -or ($EncryptedMasterSecret.Length % 2) -ne 0) {
        throw "EncryptedMasterSecret must be an even number of bytes, at least $($constant.MinimumSecretLength)."
    }

    $saltPrefix = Get-SecretSharingCipherSaltPrefix -Identifier $Identifier -Extendable:$Extendable

    $half = [int]($EncryptedMasterSecret.Length / 2)
    $l = [byte[]]$EncryptedMasterSecret[0..($half - 1)]
    $r = [byte[]]$EncryptedMasterSecret[$half..($EncryptedMasterSecret.Length - 1)]

    for ($i = $constant.RoundCount - 1; $i -ge 0; $i--) {
        $f = Invoke-SecretSharingCipherRoundFunction -RoundIndex ([byte]$i) -Passphrase $Passphrase -SaltPrefix $saltPrefix -R $r -IterationExponent $IterationExponent

        $newR = [byte[]]::new($half)
        for ($b = 0; $b -lt $half; $b++) {
            $newR[$b] = $l[$b] -bxor $f[$b]
        }

        $l = $r
        $r = $newR
    }

    return , ([byte[]]($r + $l))
}