Private/Password/Get-SecretSharingRandomAlphabetByte.ps1

function Get-SecretSharingRandomAlphabetByte {
    <#
    .SYNOPSIS
        Returns Length cryptographically random bytes, each drawn uniformly from Alphabet.
    .DESCRIPTION
        Alphabet sizes are rarely a power of two, so mapping a random byte onto Alphabet
        with a plain modulo would be biased toward its low end (256 mod Alphabet.Length
        leftover values map to fewer alphabet entries than the rest). Uses rejection
        sampling instead: any random byte at or above the largest multiple of
        Alphabet.Length that still fits in a byte is discarded and redrawn, so every
        kept byte maps to each alphabet entry with exactly equal probability.
    #>

    [CmdletBinding()]
    [OutputType([byte[]], [System.Object[]])]
    param(
        [Parameter(Mandatory)]
        [ValidateCount(1, 256)]
        [byte[]]$Alphabet,

        [Parameter(Mandatory)]
        [ValidateRange(1, [int]::MaxValue)]
        [int]$Length
    )

    $rejectionThreshold = 256 - (256 % $Alphabet.Length)
    $result = [byte[]]::new($Length)

    for ($i = 0; $i -lt $Length; $i++) {
        do {
            $candidate = (Get-SecretSharingRandomByte -Length 1)[0]
        } while ($candidate -ge $rejectionThreshold)
        $result[$i] = $Alphabet[$candidate % $Alphabet.Length]
    }

    return , $result
}