Private/Crypto/Protect-SecretSharingMasterSecret.ps1
|
function Protect-SecretSharingMasterSecret { <# .SYNOPSIS Encrypts a master secret using the SLIP-0039 4-round Feistel cipher. .DESCRIPTION Splits MasterSecret into equal left/right halves and runs 4 forward Feistel rounds keyed by Passphrase, Identifier, and IterationExponent, per SLIP-0039. An empty Passphrase is a valid input - per spec, there is deliberately no way to verify a passphrase was "correct," which enables plausible deniability. #> [CmdletBinding()] [OutputType([byte[]], [System.Object[]])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Pure in-memory computation; no external state is changed.')] param( [Parameter(Mandatory)] [byte[]]$MasterSecret, [byte[]]$Passphrase = @(), [Parameter(Mandatory)] [ValidateRange(0, 32767)] [int]$Identifier, [Parameter(Mandatory)] [ValidateRange(0, 15)] [int]$IterationExponent, [switch]$Extendable ) $constant = Get-SecretSharingCipherConstant if ($MasterSecret.Length -lt $constant.MinimumSecretLength -or ($MasterSecret.Length % 2) -ne 0) { throw "MasterSecret must be an even number of bytes, at least $($constant.MinimumSecretLength)." } $saltPrefix = Get-SecretSharingCipherSaltPrefix -Identifier $Identifier -Extendable:$Extendable $half = [int]($MasterSecret.Length / 2) $l = [byte[]]$MasterSecret[0..($half - 1)] $r = [byte[]]$MasterSecret[$half..($MasterSecret.Length - 1)] for ($i = 0; $i -lt $constant.RoundCount; $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)) } |