Protocols/MSNP/TCL (Challenge Implementation)

From NINA Wiki
Revision as of 03:22, 5 February 2022 by Animadoria (talk | contribs) (Fix formatting)
Jump to navigation Jump to search
        proc CreateQRYHash {chldata} {
                set prodid "PROD0090YUAUV\{2B"
                set prodkey "YMM8C_H7KCQ2S_KL"

                # Create an MD5 hash out of the given data, then form 32 bit integers from it
                set md5hash [::md5::md5 -hex $chldata$prodkey]
                set md5parts [MD5HashToInt $md5hash]

                # Then create a valid productid string, divisable by 8, then form 32 bit integers from it
                set nrPadZeros [expr 8 - [string length $chldata$prodid] % 8]
                set padZeros [string repeat 0 $nrPadZeros]
                set chlprodid [CHLProdToInt $chldata$prodid$padZeros]

                # Create the key we need to XOR
                set key [KeyFromInt $md5parts $chlprodid]

                set low 0x[string range $md5hash 0 15]
                set high 0x[string range $md5hash 16 32]
                set low [expr {$low ^ $key}]
                set high [expr {$high ^ $key}]

                set p1 [format %8.8x [expr {$low / 0x100000000}]]
                set p2 [format %8.8x [expr {$low % 0x100000000}]]
                set p3 [format %8.8x [expr {$high / 0x100000000}]]
                set p4 [format %8.8x [expr {$high % 0x100000000}]]

                return $p1$p2$p3$p4
        }

        proc KeyFromInt { md5parts chlprod } {
                # Create a new series of numbers
                set key_temp 0
                set key_high 0
                set key_low 0

                # Then loop on the entries in the second array we got in the parameters
                for {set i 0} {$i < [llength $chlprod]} {incr i 2} {

                        # Make $key_temp zero again and perform calculation as described in the documents
                        set key_temp [lindex $chlprod $i]
                        set key_temp [expr {(wide(0x0E79A9C1) * wide($key_temp)) % wide(0x7FFFFFFF)}]
                        set key_temp [expr {wide($key_temp) + wide($key_high)}]
                        set key_temp [expr {(wide([lindex $md5parts 0]) * wide($key_temp)) + wide([lindex $md5parts 1])}]
                        set key_temp [expr {wide($key_temp) % wide(0x7FFFFFFF)}]

                        set key_high [lindex $chlprod [expr {$i+1}]]
                        set key_high [expr {(wide($key_high) + wide($key_temp)) % wide(0x7FFFFFFF)}]
                        set key_high [expr {(wide([lindex $md5parts 2]) * wide($key_high)) + wide([lindex $md5parts 3])}]
                        set key_high [expr {wide($key_high) % wide(0x7FFFFFFF)}]
                        set key_low [expr {wide($key_low) + wide($key_temp) + wide($key_high)}]
                }

                set key_high [expr {(wide($key_high) + wide([lindex $md5parts 1])) % wide(0x7FFFFFFF)}]
                set key_low [expr {(wide($key_low) + wide([lindex $md5parts 3])) % wide(0x7FFFFFFF)}]

                set key_high 0x[byteInvert [format %8.8X $key_high]]
                set key_low 0x[byteInvert [format %8.8X $key_low]]

                set long_key [expr {(wide($key_high) << 32) + wide($key_low)}]

                return $long_key
        }

        # Takes an CHLData + ProdID + Padded string and chops it in 4 bytes. Then converts to 32 bit integers
        proc CHLProdToInt { CHLProd } {
                set hexs {}
                set result {}
                while {[string length $CHLProd] > 0} {
                        lappend hexs [string range $CHLProd 0 3]
                        set CHLProd [string range $CHLProd 4 end]
                }
                for {set i 0} {$i < [llength $hexs]} {incr i} {
                        binary scan [lindex $hexs $i] H8 int
                        lappend result 0x[byteInvert $int]
                }
                return $result
        }

        # Takes an MD5 string and chops it in 4. Then "decodes" the HEX and converts to 32 bit integers. After that it ANDs
        proc MD5HashToInt { md5hash } {
                binary scan $md5hash a8a8a8a8 hash1 hash2 hash3 hash4
                set hash1 [expr "0x[byteInvert $hash1]" & 0x7FFFFFFF]
                set hash2 [expr "0x[byteInvert $hash2]" & 0x7FFFFFFF]
                set hash3 [expr "0x[byteInvert $hash3]" & 0x7FFFFFFF]
                set hash4 [expr "0x[byteInvert $hash4]" & 0x7FFFFFFF]

                return [list $hash1 $hash2 $hash3 $hash4]
        }

        proc byteInvert { hex } {
                set hexs {}
                while {[string length $hex] > 0} {
                        lappend hexs [string range $hex 0 1]
                        set hex [string range $hex 2 end]
                }
                set hex ""
                for {set i [expr [llength $hexs] -1]} {$i >= 0} {incr i -1} {
                        append hex [lindex $hexs $i]
                }
                return $hex
        }