Valhalla Legends Forums Archive | Battle.net Bot Development | Translating procW3Key from mbncsutil

AuthorMessageTime
Strilanc
I've translated the war3 cd key decoder from mbncsutil into vb.net. But I can't seem to get correct results. The compiled library works fine, so I know I've made an error somewhere. I don't have access to a C# interpreter so I can't see what values should be half way through the computation. This is a learning experience for me, so I'd like to figure out what I'm doing wrong. It may be a blind oversight on my part, or some obscure difference between vb and c#.

I did manage to totally rewrite the decodeKeyTable method into something legible, so that's something.

I'm not quite sure what my question is, I just want general help. If someone could tell me the true values of srcData before and after the //mult block for a known cd key, that would be very helpful.
January 2, 2008, 2:17 AM
FrostWraith
I am not going to pretend to know exactly how to help you, but you will need to show some code and how you are testing it to receive any help.
January 2, 2008, 2:52 AM
Strilanc
Sure thing, I just didn't know if it was appropriate to post so much code so soon.

Here is the relevant code:

[code]
    'in a module
    'easy ways to create arrays
    Public Function bunch(Of x)(ByVal ParamArray xx As x()) As x()
        Return xx
    End Function
    Public Function bytes(ByVal ParamArray bb As Byte()) As Byte()
        Return bb
    End Function

    'in the decoding class
    '30 permutations of 0-15 (still the same map, just divided in chunks of 16)
    Private Shared w3TranslateMap As Byte()() = bunch( _
                bytes(&H9, &H4, &H7, &HF, &HD, &HA, &H3, &HB, &H1, &H2, &HC, &H8, &H6, &HE, &H5, &H0), _
                bytes(&H9, &HB, &H5, &H4, &H8, &HF, &H1, &HE, &H7, &H0, &H3, &H2, &HA, &H6, &HD, &HC), _
                bytes(&HC, &HE, &H1, &H4, &H9, &HF, &HA, &HB, &HD, &H6, &H0, &H8, &H7, &H2, &H5, &H3), _
                bytes(&HB, &H2, &H5, &HE, &HD, &H3, &H9, &H0, &H1, &HF, &H7, &HC, &HA, &H6, &H4, &H8), _
                bytes(&H6, &H2, &H4, &H5, &HB, &H8, &HC, &HE, &HD, &HF, &H7, &H1, &HA, &H0, &H3, &H9), _
                bytes(&H5, &H4, &HE, &HC, &H7, &H6, &HD, &HA, &HF, &H2, &H9, &H1, &H0, &HB, &H8, &H3), _
                bytes(&HC, &H7, &H8, &HF, &HB, &H0, &H5, &H9, &HD, &HA, &H6, &HE, &H2, &H4, &H3, &H1), _
                bytes(&H3, &HA, &HE, &H8, &H1, &HB, &H5, &H4, &H2, &HF, &HD, &HC, &H6, &H7, &H9, &H0), _
                bytes(&HC, &HD, &H1, &HF, &H8, &HE, &H5, &HB, &H3, &HA, &H9, &H0, &H7, &H2, &H4, &H6), _
                bytes(&HD, &HA, &H7, &HE, &H1, &H6, &HB, &H8, &HF, &HC, &H5, &H2, &H3, &H0, &H4, &H9), _
                bytes(&H3, &HE, &H7, &H5, &HB, &HF, &H8, &HC, &H1, &HA, &H4, &HD, &H0, &H6, &H9, &H2), _
                bytes(&HB, &H6, &H9, &H4, &H1, &H8, &HA, &HD, &H7, &HE, &H0, &HC, &HF, &H2, &H3, &H5), _
                bytes(&HC, &H7, &H8, &HD, &H3, &HB, &H0, &HE, &H6, &HF, &H9, &H4, &HA, &H1, &H5, &H2), _
                bytes(&HC, &H6, &HD, &H9, &HB, &H0, &H1, &H2, &HF, &H7, &H3, &H4, &HA, &HE, &H8, &H5), _
                bytes(&H3, &H6, &H1, &H5, &HB, &HC, &H8, &H0, &HF, &HE, &H9, &H4, &H7, &HA, &HD, &H2), _
                bytes(&HA, &H7, &HB, &HF, &H2, &H8, &H0, &HD, &HE, &HC, &H1, &H6, &H9, &H3, &H5, &H4), _
                bytes(&HA, &HB, &HD, &H4, &H3, &H8, &H5, &H9, &H1, &H0, &HF, &HC, &H7, &HE, &H2, &H6), _
                bytes(&HB, &H4, &HD, &HF, &H1, &H6, &H3, &HE, &H7, &HA, &HC, &H8, &H9, &H2, &H5, &H0), _
                bytes(&H9, &H6, &H7, &H0, &H1, &HA, &HD, &H2, &H3, &HE, &HF, &HC, &H5, &HB, &H4, &H8), _
                bytes(&HD, &HE, &H5, &H6, &H1, &H9, &H8, &HC, &H2, &HF, &H3, &H7, &HB, &H4, &H0, &HA), _
                bytes(&H9, &HF, &H4, &H0, &H1, &H6, &HA, &HE, &H2, &H3, &H7, &HD, &H5, &HB, &H8, &HC), _
                bytes(&H3, &HE, &H1, &HA, &H2, &HC, &H8, &H4, &HB, &H7, &HD, &H0, &HF, &H6, &H9, &H5), _
                bytes(&H7, &H2, &HC, &H6, &HA, &H8, &HB, &H0, &HF, &H4, &H3, &HE, &H9, &H1, &HD, &H5), _
                bytes(&HC, &H4, &H5, &H9, &HA, &H2, &H8, &HD, &H3, &HF, &H1, &HE, &H6, &H7, &HB, &H0), _
                bytes(&HA, &H8, &HE, &HD, &H9, &HF, &H3, &H0, &H4, &H6, &H1, &HC, &H7, &HB, &H2, &H5), _
                bytes(&H3, &HC, &H4, &HA, &H2, &HF, &HD, &HE, &H7, &H0, &H5, &H8, &H1, &H6, &HB, &H9), _
                bytes(&HA, &HC, &H1, &H0, &H9, &HE, &HD, &HB, &H3, &H7, &HF, &H8, &H5, &H2, &H4, &H6), _
                bytes(&HE, &HA, &H1, &H8, &H7, &H6, &H5, &HC, &H2, &HF, &H0, &HD, &H3, &HB, &H4, &H9), _
                bytes(&H3, &H8, &HE, &H0, &H7, &H9, &HF, &HC, &H1, &H6, &HD, &H2, &H5, &HA, &HB, &H4), _
                bytes(&H3, &HA, &HC, &H4, &HD, &HB, &H9, &HE, &HF, &H6, &H1, &H7, &H2, &H0, &H5, &H8) _
            )
    'Returns the product, public and private keys of a cd key
    Public Shared Function processWC3Key(ByVal key As String) As Byte()()
        Const W3_KEYLEN As Integer = 26
        Const W3_BUFLEN As Integer = W3_KEYLEN * 2
        If key.Length <> W3_KEYLEN Then Throw New Exception("Invalid cd key length.")
        Dim table(0 To W3_BUFLEN - 1) As Byte
        Dim cdkey As Char() = key.ToUpper().ToCharArray()

        'Fill table with permuted cd key
        Dim a As Integer = 0, b As Integer = &H21
        For i As Integer = 0 To key.Length - 1
            a = (b + &H7B5) Mod W3_BUFLEN
            b = (a + &H7B5) Mod W3_BUFLEN
            Dim c As Byte = w3KeyMap(Asc(cdkey(i))) '0 to 25
            If c = 255 Then Throw New Exception("Invalid cd key character.")
            table(a) = CByte(c / 5)
            table(b) = CByte(c Mod 5)
        Next i

        'Compute table values
        '(this section is very likely incorrect)
        Dim values(0 To 3) As UInteger
        Dim n As BigNum = New BigNum(0)
        For i As Integer = W3_BUFLEN - 1 To 0 Step -1
            n += table(i)
            n *= 5
            ReDim Preserve n.bytes(0 To 15)
        Next i
        Dim bbb()() As Byte = chopBytes(n.bytes, 4, 4, 4, 4)
        For i As Integer = 0 To 3
            values(i) = CUInt(unpackLong(bbb(i)))
        Next i
        decodeKeyTable(values)

        Dim bbProduct() As Byte = packLong(CLng(values(0) >> &HA))
        Dim bbPublic() As Byte = reverseBytes(packLong(values(0) And &HFFFFFF00))
        Dim bbPrivate() As Byte = packBytes(packLong(values(1) And &HFFFFFF, 3), packLong(values(2)), packLong(values(3) >> 2, 3))

        Return bunch(bbProduct, bbPublic, bbPrivate)
    End Function

    'Almost totally rewritten. Equivalent to an almost word-for-word translation
    Private Shared Sub decodeKeyTable(ByVal keyTable As UInteger())
        Dim keys(0 To 29) As Integer, shifts(0 To 29) As Integer

        'Pre-compute key indices and shifts for each row for the first pass
        For r As Integer = 0 To 29
            'note that 0-29 use only 5 bits
            keys(r) = 3 - (r >> 3) '0 to 3; negation of the 2 high bits of row
            shifts(r) = (r And &H7) << 2 '0 to 28; 4x the 3 low bits of row
        Next r

        'First pass
        For r As Integer = 29 To 0 Step -1
            Dim perm() As Byte = w3TranslateMap(r)
            Dim c As Integer = CInt((keyTable(keys(r)) >> shifts(r)) And &HF) '0 to 15; pick 4 adjacent bits of keyTable(keys(r))

            'Jump around the w3TranslateMap columns a bit
            For r2 As Integer = 29 To 0 Step -1
                If r = r2 Then Continue For
                Dim c2 As Integer = CInt((keyTable(keys(r2)) >> shifts(r2)) And &HF) '0 to 15; pick 4 adjacent bits
                c = perm(c2 Xor perm(c)) '0 to 15; permute column
            Next r2

            'Replace the 4 adjacent bits we picked in keyTable(keys(r)) with the 4 bits of perm(c)
            keyTable(keys(r)) = CUInt(keyTable(keys(r)) And Not (&HF << shifts(r)))
            keyTable(keys(r)) = CUInt(keyTable(keys(r)) Or (CUInt(perm(c)) << shifts(r)))
        Next r

        'Store the keyTable so far
        Dim keyTableFirstPass(0 To 3) As UInteger
        keyTable.CopyTo(keyTableFirstPass, 0)

        'Second pass
        For i As Integer = 0 To 119
            'note that 0-119 use only 7 bits
            Dim j As Integer = (i * 11) Mod 120
            Dim iKey As Integer = 3 - (i >> 5) '0 to 3; the 2 high bits of i inverted
            Dim jKey As Integer = 3 - (j >> 5) '0 to 3; the 2 high bits of j inverted
            Dim iShift As Integer = i And &H1F '0 to 31; the 5 low bits of i
            Dim jShift As Integer = j And &H1F '0 to 31; the 5 low bits of j

            'replace the iShift'th bit of keyTable(iKey) with the jShift'th bit of keyTableFirstPass(jKey)
            If ((keyTableFirstPass(jKey) >> jShift) And &H1) = 0 Then
                keyTable(iKey) = CUInt(keyTable(iKey) And Not (CUInt(&H1) << iShift)) 'unset
            Else
                keyTable(iKey) = CUInt(keyTable(iKey) Or (CUInt(&H1) << iShift)) 'set
            End If
        Next i
    End Sub
[/code]

The pack and unpack functions just convert numbers to and from arrays of bytes. I'm testing it by running it in the visual studio interpreter and comparing the public/product keys to the known values I sniffed using wireshark.
January 2, 2008, 4:47 AM
Strilanc
I've solved the problem. I needed to switch endian-ness before the multiplication step, among other small things. Now I can see that the CD Key is shuffled into a base 5 number, then converted to base 2, then some cryptography stuff, then the keys come right out.
January 4, 2008, 9:21 PM
Strilanc
As another update, I've massively improved on the code originally provided. It is now trivial to reverse the process and generate a cd key from given public/private/product keys (of course such keys won't necessarily work on BNET because not all public/private key pairs are valid).

[code]
    ''' <summary>Map from cd key characters to their values (invalid characters go to 0xFF)</summary>
    ''' <remarks>Only 25 characters from the possible 36</remarks>
    Private Shared ReadOnly w3KeyMap As Byte() = { _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &H0, &HFF, &H1, &HFF, &H2, &H3, &H4, &H5, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &H6, &H7, &H8, &H9, &HA, &HB, &HC, &HFF, &HD, &HE, &HFF, &HF, &H10, &HFF, _
            &H11, &HFF, &H12, &HFF, &H13, &HFF, &H14, &H15, &H16, &H17, &H18, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &H6, &H7, &H8, &H9, &HA, &HB, &HC, &HFF, &HD, &HE, &HFF, &HF, &H10, &HFF, _
            &H11, &HFF, &H12, &HFF, &H13, &HFF, &H14, &H15, &H16, &H17, &H18, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _
            &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF _
        }
    ''' <summary>30 permutations of 0-15</summary>
    Private Shared ReadOnly w3TranslateMap As Byte()() = { _
                array(Of Byte)(&H9, &H4, &H7, &HF, &HD, &HA, &H3, &HB, &H1, &H2, &HC, &H8, &H6, &HE, &H5, &H0), _
                array(Of Byte)(&H9, &HB, &H5, &H4, &H8, &HF, &H1, &HE, &H7, &H0, &H3, &H2, &HA, &H6, &HD, &HC), _
                array(Of Byte)(&HC, &HE, &H1, &H4, &H9, &HF, &HA, &HB, &HD, &H6, &H0, &H8, &H7, &H2, &H5, &H3), _
                array(Of Byte)(&HB, &H2, &H5, &HE, &HD, &H3, &H9, &H0, &H1, &HF, &H7, &HC, &HA, &H6, &H4, &H8), _
                array(Of Byte)(&H6, &H2, &H4, &H5, &HB, &H8, &HC, &HE, &HD, &HF, &H7, &H1, &HA, &H0, &H3, &H9), _
                array(Of Byte)(&H5, &H4, &HE, &HC, &H7, &H6, &HD, &HA, &HF, &H2, &H9, &H1, &H0, &HB, &H8, &H3), _
                array(Of Byte)(&HC, &H7, &H8, &HF, &HB, &H0, &H5, &H9, &HD, &HA, &H6, &HE, &H2, &H4, &H3, &H1), _
                array(Of Byte)(&H3, &HA, &HE, &H8, &H1, &HB, &H5, &H4, &H2, &HF, &HD, &HC, &H6, &H7, &H9, &H0), _
                array(Of Byte)(&HC, &HD, &H1, &HF, &H8, &HE, &H5, &HB, &H3, &HA, &H9, &H0, &H7, &H2, &H4, &H6), _
                array(Of Byte)(&HD, &HA, &H7, &HE, &H1, &H6, &HB, &H8, &HF, &HC, &H5, &H2, &H3, &H0, &H4, &H9), _
                array(Of Byte)(&H3, &HE, &H7, &H5, &HB, &HF, &H8, &HC, &H1, &HA, &H4, &HD, &H0, &H6, &H9, &H2), _
                array(Of Byte)(&HB, &H6, &H9, &H4, &H1, &H8, &HA, &HD, &H7, &HE, &H0, &HC, &HF, &H2, &H3, &H5), _
                array(Of Byte)(&HC, &H7, &H8, &HD, &H3, &HB, &H0, &HE, &H6, &HF, &H9, &H4, &HA, &H1, &H5, &H2), _
                array(Of Byte)(&HC, &H6, &HD, &H9, &HB, &H0, &H1, &H2, &HF, &H7, &H3, &H4, &HA, &HE, &H8, &H5), _
                array(Of Byte)(&H3, &H6, &H1, &H5, &HB, &HC, &H8, &H0, &HF, &HE, &H9, &H4, &H7, &HA, &HD, &H2), _
                array(Of Byte)(&HA, &H7, &HB, &HF, &H2, &H8, &H0, &HD, &HE, &HC, &H1, &H6, &H9, &H3, &H5, &H4), _
                array(Of Byte)(&HA, &HB, &HD, &H4, &H3, &H8, &H5, &H9, &H1, &H0, &HF, &HC, &H7, &HE, &H2, &H6), _
                array(Of Byte)(&HB, &H4, &HD, &HF, &H1, &H6, &H3, &HE, &H7, &HA, &HC, &H8, &H9, &H2, &H5, &H0), _
                array(Of Byte)(&H9, &H6, &H7, &H0, &H1, &HA, &HD, &H2, &H3, &HE, &HF, &HC, &H5, &HB, &H4, &H8), _
                array(Of Byte)(&HD, &HE, &H5, &H6, &H1, &H9, &H8, &HC, &H2, &HF, &H3, &H7, &HB, &H4, &H0, &HA), _
                array(Of Byte)(&H9, &HF, &H4, &H0, &H1, &H6, &HA, &HE, &H2, &H3, &H7, &HD, &H5, &HB, &H8, &HC), _
                array(Of Byte)(&H3, &HE, &H1, &HA, &H2, &HC, &H8, &H4, &HB, &H7, &HD, &H0, &HF, &H6, &H9, &H5), _
                array(Of Byte)(&H7, &H2, &HC, &H6, &HA, &H8, &HB, &H0, &HF, &H4, &H3, &HE, &H9, &H1, &HD, &H5), _
                array(Of Byte)(&HC, &H4, &H5, &H9, &HA, &H2, &H8, &HD, &H3, &HF, &H1, &HE, &H6, &H7, &HB, &H0), _
                array(Of Byte)(&HA, &H8, &HE, &HD, &H9, &HF, &H3, &H0, &H4, &H6, &H1, &HC, &H7, &HB, &H2, &H5), _
                array(Of Byte)(&H3, &HC, &H4, &HA, &H2, &HF, &HD, &HE, &H7, &H0, &H5, &H8, &H1, &H6, &HB, &H9), _
                array(Of Byte)(&HA, &HC, &H1, &H0, &H9, &HE, &HD, &HB, &H3, &H7, &HF, &H8, &H5, &H2, &H4, &H6), _
                array(Of Byte)(&HE, &HA, &H1, &H8, &H7, &H6, &H5, &HC, &H2, &HF, &H0, &HD, &H3, &HB, &H4, &H9), _
                array(Of Byte)(&H3, &H8, &HE, &H0, &H7, &H9, &HF, &HC, &H1, &H6, &HD, &H2, &H5, &HA, &HB, &H4), _
                array(Of Byte)(&H3, &HA, &HC, &H4, &HD, &HB, &H9, &HE, &HF, &H6, &H1, &H7, &H2, &H0, &H5, &H8) _
            }

    ''' <summary>Extracts the product, public and private keys from a wc3 cd key.</summary>
    ''' <param name="key">The cd key. Dashes and lower case characters are allowed.</param>
    ''' <returns>Byte arrays for each key: return.v1 = product key, return.v2 = public key, return.v3 = private key</returns>
    ''' <remarks>All arrays and numbers used are considered as big endian</remarks>
    Public Shared Function decodeWC3Key(ByVal key As String) As Tuple(Of Byte(), Byte(), Byte())
        Const W3_KEYLEN As Integer = 26
        Const W3_BUFLEN As Integer = W3_KEYLEN * 2
        If key.Length <> W3_KEYLEN Then Throw New ArgumentException("Invalid cd key length.")
        Dim cdkey As Char() = key.ToUpper().Replace("-", "").ToCharArray()

        'Shuffle the cd key into the digits of a base 5 number
        Dim d As Integer = 33
        Dim n_digitsBase5(0 To W3_BUFLEN - 1) As Byte
        For i As Integer = 0 To W3_KEYLEN - 1
            Dim c As Byte = w3KeyMap(Asc(cdkey(i))) 'will be from 0 to 24, or invalid (0xFF)
            If c = &HFF Then Throw New ArgumentException("Invalid cd key character: " + cdkey(i))

            'get the two base 5 digits of c
            d = (d + 49) Mod W3_BUFLEN
            n_digitsBase5(d) = CByte(c \ 5)
            d = (d + 49) Mod W3_BUFLEN
            n_digitsBase5(d) = CByte(c Mod 5)
        Next i

        'Convert the shuffled number to base 2
        'Note that a 52 digit base 5 number needs at most 121 bits
        Dim n As BigNum = 0
        For i As Integer = W3_BUFLEN - 1 To 0 Step -1
            n *= 5
            n += n_digitsBase5(i)
        Next i
        Dim nbytes() As Byte = n.getBytes(16)

        'Convert from bytes to nibbles
        Dim nibbles(0 To 31) As Byte
        For i As Integer = 0 To 15
            For j As Integer = 0 To 1
                nibbles((i << 1) + j) = CByte((nbytes(i) >> (j << 2)) And CUInt(&HF))
            Next j
        Next i

        'Permute the nibbles using the translation map
        For r As Integer = 29 To 0 Step -1
            Dim perm() As Byte = w3TranslateMap(r)
            Dim c As Byte = nibbles(r)

            'Permute
            For r2 As Integer = 29 To 0 Step -1
                If r = r2 Then Continue For
                c = perm(nibbles(r2) Xor perm(c))
            Next r2

            nibbles(r) = perm(c)
        Next r

        'Convert from nibbles to bits
        Dim bits(0 To 127) As Boolean
        For i As Integer = 0 To 31
            For j As Integer = 0 To 3
                bits(i * 4 + j) = CBool(((nibbles(i) >> j) And &H1) <> 0)
            Next j
        Next i

        'Swap the bits with their *11 counterpart (mod 120)
        'Note that x*11*11 = x*121 = x*1 = x (mod 120)
        For i As Integer = 0 To 119
            Dim j As Integer = (i * 11) Mod 120
            If j <= i Then Continue For 'don't swap the same bits twice

            'swap bits i and j
            Dim b As Boolean = bits(i)
            bits(i) = bits(j)
            bits(j) = b
        Next i

        'Convert from bits to bytes
        Dim bb(0 To 15) As Byte
        For i As Integer = 0 To 15
            For j As Integer = 0 To 7
                If bits((i << 3) + j) Then bb(i) = bb(i) Or CByte(&H1 << j)
            Next j
        Next i

        'Extract keys
        Dim bbProduct() As Byte, bbPublic() As Byte, bbPrivate() As Byte
        bbProduct = array(Of Byte)(bb(13) >> &H2, 0, 0, 0)
        bbPublic = array(Of Byte)(bb(10), bb(11), bb(12), 0)
        bbPrivate = array(Of Byte)(bb(8), bb(9), bb(4), bb(5), bb(6), bb(7), bb(0), bb(1), bb(2), bb(3))

        Return New Tuple(Of Byte(), Byte(), Byte())(bbProduct, bbPublic, bbPrivate)
    End Function
[/code]
January 27, 2008, 6:51 PM

Search