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
}