Private/ConvertTo-EFIpNetwork.ps1

function ConvertTo-EFIpNetwork {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Cidr
    )

    if ($Cidr.Length -gt 128 -or $Cidr -notmatch '^[0-9A-Fa-f:./]+$') {
        throw [System.ArgumentException]::new(
            'Each CIDR range must be 128 characters or fewer and contain only IP-address, slash, and prefix characters without whitespace or control characters.'
        )
    }

    $parts = @($Cidr.Split([char]'/'))
    if ($parts.Count -ne 2 -or [string]::IsNullOrWhiteSpace($parts[0]) -or
        $parts[1] -notmatch '^(?:0|[1-9][0-9]{0,2})$') {
        throw [System.ArgumentException]::new(
            "Each CIDR range must contain one IP address and one prefix length separated by '/'."
        )
    }

    $addressText = [string]$parts[0]
    $prefixLength = [int]$parts[1]
    $parsedAddress = $null
    $family = $null
    if ($addressText.Contains(':')) {
        if ($addressText -notmatch '^[0-9A-Fa-f:]+$' -or $addressText.Contains('.') -or
            $addressText.Contains('%') -or
            -not [Net.IPAddress]::TryParse($addressText, [ref]$parsedAddress) -or
            $parsedAddress.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetworkV6 -or
            $parsedAddress.IsIPv4MappedToIPv6 -or $parsedAddress.ScopeId -ne 0 -or
            $prefixLength -lt 1 -or $prefixLength -gt 128) {
            throw [System.ArgumentException]::new(
                'Each IPv6 CIDR range must use one ordinary network address with a prefix from 1 through 128. Scope IDs and embedded IPv4 forms are not accepted.'
            )
        }
        $family = 'IPv6'
    }
    else {
        if ($addressText -notmatch '^(?:0|[1-9][0-9]{0,2})(?:\.(?:0|[1-9][0-9]{0,2})){3}$') {
            throw [System.ArgumentException]::new(
                'Each IPv4 CIDR range must use four decimal numbers without leading zeros.'
            )
        }
        foreach ($octet in @($addressText.Split([char]'.'))) {
            if ([int]$octet -gt 255) {
                throw [System.ArgumentException]::new(
                    'Each IPv4 CIDR number must be from 0 through 255.'
                )
            }
        }
        if (-not [Net.IPAddress]::TryParse($addressText, [ref]$parsedAddress) -or
            $parsedAddress.AddressFamily -ne [Net.Sockets.AddressFamily]::InterNetwork -or
            $prefixLength -lt 1 -or $prefixLength -gt 32) {
            throw [System.ArgumentException]::new(
                'Each IPv4 CIDR range must use one network address with a prefix from 1 through 32.'
            )
        }
        $family = 'IPv4'
    }

    [byte[]]$addressBytes = $parsedAddress.GetAddressBytes()
    for ($index = 0; $index -lt $addressBytes.Length; $index++) {
        $bitsBeforeByte = $index * 8
        $bitsInByte = [math]::Min(8, [math]::Max(0, $prefixLength - $bitsBeforeByte))
        $networkMask = if ($bitsInByte -eq 0) {
            [byte]0
        }
        elseif ($bitsInByte -eq 8) {
            [byte]255
        }
        else {
            [byte](256 - (1 -shl (8 - $bitsInByte)))
        }
        $hostMask = [byte](255 - $networkMask)
        if (($addressBytes[$index] -band $hostMask) -ne 0) {
            throw [System.ArgumentException]::new(
                "Each CIDR range must begin with the network address. Clear the host bits after the '/$prefixLength' prefix."
            )
        }
    }

    [pscustomobject]@{
        Family        = $family
        PrefixLength  = $prefixLength
        NetworkBytes  = [byte[]]$addressBytes
        CanonicalKey  = '{0}|{1}|{2}' -f $family, $prefixLength,
            [Convert]::ToBase64String([byte[]]$addressBytes)
    }
}