bookmark_borderPowershell IPv4 to IPv6 and Expand IPv6

Here is the easy answer using built in .NET functions:

#
# Converts an IPV4 address to an IPV6 address.
#
function Map-IPv4ToIPv6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
                   Position = 0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)][String]$IPv4
    )
    process
    {
        [ipaddress]$parsedAddress = $null;
        if (-not [ipaddress]::TryParse($IPv4, [ref]$parsedAddress))
        {
            throw "Could not parse IPV4 address $IPv4"
        }

        # Already done
        if ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6)
        {
            return $parsedAddress;
        }
        elseif ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork)
        {
            return $parsedAddress.MapToIPv6();
        }
    }
}

#
# Converts an IPV6 address to an IPV4 address.
#
function Map-IPv6ToIPv4
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
                   Position = 0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        [ipaddress]$parsedAddress = $null;
        if (-not [ipaddress]::TryParse($IPv6, [ref]$parsedAddress))
        {
            throw "Could not parse IPv6 address $IPv6"
        }

        # Already done
        if ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork)
        {
            return $parsedAddress;
        }
        elseif ($parsedAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6)
        {
            return $parsedAddress.MapToIPv4();
        }
    }
}

For a more detailed, and “manual” approach, you can keep reading.

IPv4 is simple, 32 bits of easy to understand goodness. Every single IPv4 address is made up of 4 bytes. For example “192.168.1.1”. There is no shorthand either, that is the address, that’s it. There is no better way to write it. Ok, so now that that is out of the picture, what is IPv6.

IPv6 is 128 bits of a little bit more confusing. Well actually, it’s not the address that is confusing, it’s the notation. When you see IPv6, a lot of times you see things like “::1” and “FE80::1”. What does that even mean. How is that 128 bits. How will we ever know. So the secret is in the “::”, which is pretty much just shorthand for “a lot of zeros go here”.

For example: if we take the address “::1”, it actually expands out to “0000:0000:0000:0000:0000:0000:0000:0001”. Literally just means fill in this space with zeros. Another example: “FE80::1” becomes “FE80:0000:0000:0000:0000:0000:0000:0001”. Just FYI, you are only allowed to use the “::” once, otherwise addresses would be unreadable. (“::1::”) So before anything, here is a function to automate that, because it drives me nuts.

#
# Expand an IPv6 address. For example ::1 becomes 0000:0000:0000:0000:0000:0000:0000:0001
#
function Expand-IPV6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
                   Position = 0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        $count = 0
        $loc = -1

        # Count the number of colons, and keep track of the double colon
        for($i = 0; $i -lt $IPv6.Length; $i++) 
        { 
            if($IPv6[$i] -eq ':') 
            {
                $count++
                if(($i - 1) -ge 0 -and $IPv6[$i-1] -eq ':')
                {
                    $loc = $i
                }
            }
        }

        # If we didnt find a double colon and the count isn't 7, then throw an exception
        if($loc -lt 0 -and $count -ne 7)
        {
            throw "Invalid IPv6 Address"
        }

        # Add in any missing colons if we had a double
        $cleaned = $IPv6
        if($count -lt 7)
        {
            $cleaned = $IPv6.Substring(0, $loc) + (':'*(7-$count)) + $IPv6.Substring($loc)
        }

        # Parse current values in fill in new IP with hex numbers padded to 4 digits
        $result = @()
        foreach($splt in $cleaned -split ':')
        {
            $val = 0
            $r = [int]::TryParse($splt, [System.Globalization.NumberStyles]::HexNumber, [System.Globalization.CultureInfo]::InvariantCulture, [ref]$val)
            $result += ('{0:X4}' -f $val)
        }

        return $result -join ':'
    }
}

So this does the expansions work for us, because it’s a pain in the butt to deal with these addresses in their short form. Also, I know the code is gross and I apologize for that, but there is more to come. Ok, so now, what happens if we have an IPv4 address and we need to turn it into IPv6. Boom, its easy. The last 32 bits of the IPv6 address are equal to our IPv4 address, and the 16 bits preceding those are all set to 1. Easy enough! That means, the IP address 1.1.1.1 would turn into “::FFFF:0101:0101” or “0000:0000:0000:0000:0000:FFFF:0101:0101” in full form. So how do we make that happen?

# Map an IPv4 address to IPv6
function Map-IPv4ToIPv6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
            Position = 0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)][String]$IPv4
    )
    process
    {
        # Split on the dots
        $split = $IPv4 -split '\.'
        if($split.Length -ne 4)
        {
            throw 'Not a valid IPv4 Address'
        }

        # Parse into numbers
        $vals = @()
        foreach($v in $split)
        {
            $vals += [int]::Parse($v)
        }

        # Return as shorthand ipv6
        return "::FFFF:{0:X2}{1:X2}:{2:X2}{3:X2}" -f $vals
    }
}

BOOM, mission accomplished. Hopefully (it worked in a few tests). Also, for completeness, here is a function which turns an IPv4 address which is represented in IPv6 back into an IPv4 address.

#
# Transforms an IPv4 address which is represented in IPv6 back into IPv4
#
function Map-IPv6ToIPv4
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
            Position = 0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        # Get the expanded form
        $expanded = Expand-IPV6 $IPv6
        $split = $expanded.Split(':')

        # Make sure this is valid...
        $is4MappedTo6 = $split[5] -match 'FFFF'
        if(-not $is4MappedTo6)
        {
            throw 'This is not an IPv4 Address mapped to IPv6'
        }

        # Parse each byte as an integer
        $addr = @()
        for($i = 0; $i -lt 8; $i+=2)
        {
            $addr += [int]::Parse(($split[6 + [math]::Floor(($i / 4))][($i % 4)..(($i%4)+1)] -join ''), [System.Globalization.NumberStyles]::HexNumber)
        }

        return $addr -join '.'
    }
}

The classes built in to handle IP addresses are definitely much smarter, and the way to go, but hopefully this has explained an easy way to do it.

bookmark_borderPowershell Sha1 Implementation

This is an implementation of the SHA1 cryptographic hash algorithm in Powershell. It was a little harder than it seems, since Powershell naturally assumes all numbers are signed. Lets take for example: 0xFFFFFFFF (the maximum 32 bit unsigned int), which one would assume is equal to 4294967295. Well.. Check out this simple line of code.

#This wont throw an exception right?
[uint32]0xFFFFFFFF

Well, in PowerShell this just throws an exception. Why!? Because under the hood, PowerShell thinks: “0xFFFFFFFF”, that’s 32 bits. Let me stick it in a 32 bit signed integer. But wait… “0xFFFFFFFF” is just 32 1s. So in 2s complement, that’s -1. So in PowerShell 0xFFFFFFFF == -1. So now its pretty clear why the above code throws an exception, there is no unsigned value to represent -1. For an example, just go type “0xFFFFFFFF” into PowerShell. So.. because of that, everything here has to be done in a 64 bit integer.

#
# Implemenation of SHA1
#
function SHA1
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory = $false, Position=1)][string]$Str
    )
    process
    {
        #
        # Truncate a value to UInt32
        #
        function TUI
        {
            param($val)
            process
            {
                [uint64]($val -band (((-bnot [uint64]0)) -shr 32))
            }
        }

        #
        # Get a 32 bit value from a byte array
        #
        function GetBigEndianUInt
        {
            param
            (
                [byte[]]$bytes,
                [int]$index
            )
            process
            {
                return ([uint64]$bytes[$index] -shl 24) -bor ([uint64]$bytes[$index+1] -shl 16) -bor ([uint64]$bytes[$index+2] -shl 8) -bor ([uint64]$bytes[$index+3])
            }
        }

        #
        # Left rotate
        #
        function LeftRotate
        {
            param
            (
                [uint64]$val,
                [int]$amount
            )
            process
            {
                $res = TUI ([uint64]$val -shr (32 - $amount))
                $res = $res -bor ($val -shl $amount)
                return TUI $res
            }
        }

        # Initialize constants
        $h0 = TUI 0x67452301
        $h1 = TUI 0xEFCDAB89
        $h2 = TUI 0x98BADCFE
        $h3 = TUI 0x10325476
        $h4 = TUI 0xC3D2E1F0
        
        # Get the message bytes
        $message = [System.Text.ASCIIEncoding]::ASCII.GetBytes($Str)

        # Get the length in bytes which we need. Message length + 0x80 + (64bit message len)
        $len = ($message.Length + 9)
        
        # Get the padded length of our our byte array
        if($len % 64 -ne 0){
            $len += (64 - ($len % 64))
        }

        # Copy the bytes in the message to our byte array
        $bytes = ([byte[]]0 * $len)
        for($i = 0; $i -lt $message.Length; $i++){
            $bytes[$i] = [byte]$message[$i]
        }

        # Pad the message with 1000 0000
        $bytes[$i] = 128

        # The message length in bits
        $bitLen = $message.Length * 8

        # Set the last [uint64] as the messsage length. (We only do 32 bits)
        $bytes[$len-1] = [byte]($bitLen -band 0xFF)
        $bytes[$len-2] = [byte](($bitLen -shr 8) -band 0xFF)
        $bytes[$len-3] = [byte](($bitLen -shr 16) -band 0xFF)
        $bytes[$len-4] = [byte](($bitLen -shr 24) -band 0xFF)

        # Divide the message into 512 bit chunks
        for($chunk = 0; $chunk -lt $bytes.Length; $chunk += 64)
        {
            $w = ([uint64[]]0 * 80)

            # Copy the chunk into our working array as uints
            for($i = 0; $i -lt 16; $i++){
                $w[$i] = GetBigEndianUInt -bytes $bytes -index ($i*4 + $chunk)
            }

            for($i = 16; $i -lt 80; $i++){
                $w[$i] = LeftRotate -val (TUI ($w[$i-3] -bxor $w[$i-8] -bxor $w[$i-14] -bxor $w[$i-16])) -amount 1
            }

            $a = TUI $h0
            $b = TUI $h1
            $c = TUI $h2
            $d = TUI $h3
            $e = TUI $h4

            # A bunch of crazy stuff
            for($i = 0; $i -lt 80; $i++){
                $k=0
                if($i -lt 20){
                    $f = TUI (($b -band $c) -bor ((-bnot $b) -band $d))
                    $k = TUI 0x5A827999
                }
                elseif($i -lt 40){
                    $f = TUI ($b -bxor $c -bxor $d)
                    $k = TUI 0x6ED9EBA1
                }
                elseif($i -lt 60){
                    $f = TUI (($b -band $c) -bor ($b -band $d) -bor ($c -band $d))
                    $k = TUI 0x8F1BBCDC
                }
                else{
                    $f = TUI ($b -bxor $c -bxor $d)
                    $k = TUI 0xCA62C1D6
                }
            
                $temp = TUI ((LeftRotate -val $a -amount 5) + $f + $e + $k + $w[$i])

                $e = $d
                $d = $c
                $c = LeftRotate -val $b -amount 30
                $b = $a
                $a = $temp
            }

            $h0 = TUI ($h0 + $a)
            $h1 = TUI ($h1 + $b)
            $h2 = TUI ($h2 + $c)
            $h3 = TUI ($h3 + $d)
            $h4 = TUI ($h4 + $e)   
        }

        '{0:X8}{1:X8}{2:X8}{3:X8}{4:X8}' -f $h0, $h1, $h2, $h3, $h4
    }
}

There it is. I may have overused my TUI(To uint) function a little bit, since values larger than 32 bits could actually cause problems. I got most of the implementation details from the Wikipedia article on SHA1.

bookmark_borderSHA1 in F#

One of the first things that I like to do in a new language is implement SHA1 . It lets you dig into the language support for the basics.. bit shifts and simple math.

// Converts an ascii string into a byte sequence
let getBytes (input: string) = seq {for c in input -> (byte)c}

// Get a uint32 from a sequence of 4 bytes in big endian
let bytesToUi (input: seq<byte>) = 
    let i = ref 3

    // My decrementor function because I didnt realize value assignment don't return a value
    let dec value =
        let t = !value
        value := (!value - 1)
        t
    // Fold the 4 bytes into a uint
    Seq.fold (fun (acc : uint32) (b : byte) -> acc ||| (uint32(b) <<< (dec i) * 8)) 0u input

// Rotate left
let rotl (input: uint32) (amount) =
    (input <<< amount) ||| (input >>> (32 - amount))

// Runs SHA1 on a string
let sha1 (input: string) =
    // Get length of input in bytes
    let messageLen = uint32(input.Length)

    // Get length of input in bits
    let bitlen = uint64(8u * messageLen)

    // Get the amount we need to pad. Include the bit (0x80) we append to the message and the ulong at the end of the message for length
    let padlen = 
        match ((messageLen + 9u) % 64u) with
        | 0u -> 0
        | _ -> int(64u - ((messageLen + 9u) % 64u))

    // Append the bit to the end of the message
    let asciibytes = Seq.append (getBytes(input)) (Seq.singleton 0x80uy)

    // Pad with 0s. Assume the length is not going to be larger than uint32, so set the highest order 4 bytes to zero as well
    let asciibytes = Seq.append asciibytes (seq {for i in 1.. (padlen+4) -> 0uy})

    // Append 64 bits of message length
    let messageBytes = Seq.append asciibytes (seq {for i in (List.rev [0..7]) -> ((byte)(bitlen >>> (i * 8)))})

    // Constants
    let mutable h0 = 0x67452301u
    let mutable h1 = 0xEFCDAB89u
    let mutable h2 = 0x98BADCFEu
    let mutable h3 = 0x10325476u
    let mutable h4 = 0xC3D2E1F0u

    // Get the total message length
    let totalBytes = Seq.length messageBytes

    // For each 64 byte chunk
    for chunk = 0 to ((totalBytes / 64)-1) do
        let w = [| for i in 1 .. 80 -> 0u|]

        // Populate the array
        for i = 0 to 15 do
            w.[i] <- bytesToUi (Seq.take 4 (Seq.skip (i*4 + chunk * 64) messageBytes))
        for i = 16 to 79 do
            w.[i] <- rotl ((w.[i-3]) ^^^ (w.[i-8]) ^^^ (w.[i-14]) ^^^ (w.[i-16])) 1

        // Set up the locals
        let mutable a, b, c, d, e = h0, h1, h2, h3, h4
        let mutable k = 0u
        let mutable f = 0u

        for i in 0 .. 79 do
            let round = match i with
                        | m when m < 20 -> (0x5A827999u, ((b &amp;&amp;&amp; c) ||| ((~~~b) &amp;&amp;&amp; d)))
                        | m when m < 40 -> (0x6ED9EBA1u, (b ^^^ c ^^^ d))
                        | m when m < 60 -> (0x8F1BBCDCu, ((b &amp;&amp;&amp; c) ||| (b &amp;&amp;&amp; d) ||| (c &amp;&amp;&amp; d)))
                        | _ -> (0xCA62C1D6u, (b ^^^ c ^^^ d))
            let temp = (rotl a 5) + (snd round) + e + (fst round) + w.[i]
            e <- d
            d <- c
            c <- rotl b 30
            b <- a
            a <- temp

        h0 <- h0 + a
        h1 <- h1 + b
        h2 <- h2 + c
        h3 <- h3 + d
        h4 <- h4 + e

    [h0; h1; h2; h3; h4]

I am no F# expert, but there it is.