~amsn-daily/amsn/amsn-packaging

8331 by kakaroto
Add SVN $Id$ tag to the new files and add the keywords property on them...
1
::Version::setSubversionId {$Id$}
2
3
package require des
8795 by kakaroto
Use the newest and great DES package... to avoid issues with 'invalid keyset handle' when that des package gets loaded into amsn by some plugin or something
4
5
snit::type SecurityToken {
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
6
	option -name -default ""
7
	option -address -default ""
8
	option -policy -default ""
9
	option -type -default ""
10
	option -created -default ""
11
	option -expires -default ""
12
	option -ticket -default ""
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
13
	option -proof -default ""
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
14
	option -local_clock -default ""
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
15
	option -server_clock -default ""
16
17
	method IsExpired { } {
18
		if {$options(-ticket) == "" ||
19
		    $options(-local_clock) == "" ||
20
		    $options(-server_clock) == "" ||
21
		    $options(-expires) == ""} {
22
			return 1
23
		}
24
		set created_clock $options(-local_clock)
25
		set current_clock [clock seconds]
26
		set elapsed [expr {$current_clock - $created_clock}]
27
		
28
		set created 0
29
		set expires 0
30
		if { [regexp {(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})} $options(-server_clock) -> y m d h min s] } {
31
			set created [clock scan "${y}-${m}-${d} ${h}:${min}:${s}"]
32
		}
33
		if { [regexp {(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})} $options(-expires) -> y m d h min s] } {
34
			set expires [clock scan "${y}-${m}-${d} ${h}:${min}:${s}"]
35
		}
36
		if {$created == 0 || $expires == 0 } {
37
			return 1
38
		}
39
		set remaining [expr {$expires - $created}]
40
		if {$remaining < $elapsed} {
41
			return 1
42
		} else {
43
			return 0
44
		}
45
	}
46
}
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
47
48
49
# Single Sign-On Authentication method (used by MSNP15+)
50
snit::type SSOAuthentication {
51
	variable security_tokens
52
	variable soap_req ""
7885 by kakaroto
SSO auth object should cancel SOAP request if it gets destroyed + Code cleaning, removed debug outputs
53
	variable callbacks [list]
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
54
	option -username -default ""
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
55
	option -password -default ""
56
	option -nonce -default ""
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
57
	option -done -default 0
9682 by kakaroto
Add support for the profile accrual error as reported here : http://www.amsn-project.net/forums/viewtopic.php?t=7214&highlight=
58
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
59
	constructor { args } {
60
		$self configurelist $args
61
62
		set security_tokens [SecurityToken create %AUTO% -name Passport -address "http://Passport.NET/tb"]
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
63
64
		lappend security_tokens [SecurityToken create %AUTO% -name Contacts -address "contacts.msn.com" -policy "MBI"]
8460 by kakaroto
Fix the 'internal server error' users were having when using passwords with special chars and upper case letters
65
		lappend security_tokens [SecurityToken create %AUTO% -name Messenger -address "messenger.msn.com" -policy "?id=507"]
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
66
		lappend security_tokens [SecurityToken create %AUTO% -name MessengerClear -address "messengerclear.live.com" -policy "MBI_KEY_OLD"]
67
		lappend security_tokens [SecurityToken create %AUTO% -name MessengerSecure -address "messengersecure.live.com" -policy "MBI_SSL"]
68
		
69
		lappend security_tokens [SecurityToken create %AUTO% -name Spaces -address "spaces.live.com" -policy "MBI"]
70
		lappend security_tokens [SecurityToken create %AUTO% -name Storage -address "storage.msn.com" -policy "MBI"]
8318 by kakaroto
Remove unused Voice SSO token, and add storage token.. it's used for Roaming Content.. also make Roaming content use the new storage token.. it should make it more stable now.
71
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
72
	}
73
74
	destructor {
75
		catch {
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
76
			foreach token $security_tokens {
77
				$token destroy
78
			}
79
			set security_tokens ""
8849 by kakaroto
correctly reset the soap_req to avoid the invalid command name errors
80
		}
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
81
		if { $soap_req != "" } {
7885 by kakaroto
SSO auth object should cancel SOAP request if it gets destroyed + Code cleaning, removed debug outputs
82
			catch { $soap_req destroy }
83
			set soap_req ""
8849 by kakaroto
correctly reset the soap_req to avoid the invalid command name errors
84
		}
7885 by kakaroto
SSO auth object should cancel SOAP request if it gets destroyed + Code cleaning, removed debug outputs
85
		
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
86
	}
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
87
88
	method RequireSecurityToken { name command {full 0} } {
8280 by kakaroto
We can now request the full token instead of just the ticket from SSO, this is useful for proof+ticket Passport security token.
89
		set token [$self GetSecurityTokenByName $name]
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
90
		if {$token == "" || [$token IsExpired] } {
91
			lappend callbacks [list $name $command $full]
8280 by kakaroto
We can now request the full token instead of just the ticket from SSO, this is useful for proof+ticket Passport security token.
92
			$self Authenticate [list $self RequireSecurityTokenCB ]
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
93
		} else {
94
			if {$full} {
8280 by kakaroto
We can now request the full token instead of just the ticket from SSO, this is useful for proof+ticket Passport security token.
95
				eval $command [list $token]
96
			} else {
97
				eval $command [list [$token cget -ticket]]
98
			}
99
		}
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
100
	}
101
102
	method RequireSecurityTokenCB {err} {
103
		set clbks $callbacks 
8263 by kakaroto
Might prevent issue where a callback calls a function that requires a token and the token expired in the meantime...
104
		set callbacks [list]
105
		
106
		foreach callback $clbks {
107
			foreach {name command full} $callback break
8280 by kakaroto
We can now request the full token instead of just the ticket from SSO, this is useful for proof+ticket Passport security token.
108
			set token [$self GetSecurityTokenByName $name]
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
109
			if {$full} {
8280 by kakaroto
We can now request the full token instead of just the ticket from SSO, this is useful for proof+ticket Passport security token.
110
				eval $command [list $token]
111
			} else {
112
				eval $command [list [$token cget -ticket]]
113
			}
114
		}
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
115
	}
116
117
	method GetSecurityTokenByName { name } {
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
118
		foreach token $security_tokens {
119
			set n [$token cget -name]
120
			if {$n == $name } {
121
				return $token
122
			}
123
		}
124
		return ""
125
	}
126
	method GetSecurityTokenByAddress { address } {
127
		foreach token $security_tokens {
128
			set addr [$token cget -address]
129
			if {$addr == $address } {
130
				return $token
131
			}
132
		}
133
		return ""
134
	}
135
	
136
	method AuthenticateCallback { callbk soap } {
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
137
		status_log "SSO::AuthenticateCallback : $soap - [$soap GetStatus]"
8051 by kakaroto
Fix connection errors and authentication erros with MSNP15
138
		if { [$soap GetStatus] == "success" } {
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
139
			set xml  [$soap GetResponse]
140
			set server_clock [GetXmlAttribute $xml "S:Envelope:S:Header:psf:pp:psf:serverInfo" "ServerTime"]
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
141
			set i 0
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
142
			while {1} {
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
143
				set subxml [GetXmlNode $xml "S:Envelope:S:Body:wst:RequestSecurityTokenResponseCollection:wst:RequestSecurityTokenResponse" $i]
144
				incr i
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
145
				if  { $subxml == "" } {
146
					break
147
				}
148
			
149
				set address [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:wsp:AppliesTo:wsa:EndpointReference:wsa:Address"]
150
				set token [$self GetSecurityTokenByAddress $address]
151
				#puts "$subxml\n\n\n"
7885 by kakaroto
SSO auth object should cancel SOAP request if it gets destroyed + Code cleaning, removed debug outputs
152
				if {$token != "" } {
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
153
					$token configure -type [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:wst:TokenType"]
154
					$token configure -created [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:wst:LifeTime:wsu:Created"]
155
					$token configure -expires [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:wst:LifeTime:wsu:Expires"]
156
					$token configure -ticket [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:wst:RequestedSecurityToken:wsse:BinarySecurityToken"]
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
157
					if {[$token cget -ticket] == "" } {
8280 by kakaroto
We can now request the full token instead of just the ticket from SSO, this is useful for proof+ticket Passport security token.
158
						catch {$token configure -ticket [list2xml [GetXmlNode $subxml "wst:RequestSecurityTokenResponse:wst:RequestedSecurityToken:EncryptedData"]]}
8368 by kakaroto
do not crash if there's no security token : http://www.amsn-project.net/forums/viewtopic.php?t=5231&highlight=
159
					}
8280 by kakaroto
We can now request the full token instead of just the ticket from SSO, this is useful for proof+ticket Passport security token.
160
					$token configure -proof [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:wst:RequestedProofToken:wst:BinarySecret"]
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
161
					$token configure -local_clock [clock seconds]
8261 by kakaroto
Token expiration is now fixed... if we try to use an SSO token after it expired it will automatically fetch a new one and call your function when the token is renewed..
162
					$token configure -server_clock $server_clock
163
8051 by kakaroto
Fix connection errors and authentication erros with MSNP15
164
					status_log "Found security token $token for address $address" green
165
					set faultcode [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:S:Fault:faultcode"]
9682 by kakaroto
Add support for the profile accrual error as reported here : http://www.amsn-project.net/forums/viewtopic.php?t=7214&highlight=
166
					set faultstring [GetXmlEntry $subxml "wst:RequestSecurityTokenResponse:S:Fault:faultstring"]
167
					if {$faultcode == "wsse:FailedAuthentication"} {
168
						if {[string trim $faultstring] == "Profile accrual is required"} {
169
							set error 4
170
						} else {
171
							set error 5
172
						}
173
						status_log "Security token for $address had a fault ($faultcode): $faultstring" red
174
						if {[catch {eval $callbk [list $error]} result]} {
175
							bgerror $result
176
						}
177
						return
178
					}
179
				}
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
180
			}
181
			if {[catch {eval $callbk [list 0]} result]} {
182
				bgerror $result
183
			}
184
		} elseif  {[$soap GetStatus] == "fault" } {
8235 by kakaroto
change soap API.. GetStatus will be 'fault' in case of a soap fault, and GetLastError becomes the faultcode
185
			set faultcode [$soap GetFaultCode]
8237 by kakaroto
Add Fault handling in soap
186
			set faultstring [$soap GetFaultString]
187
			
8235 by kakaroto
change soap API.. GetStatus will be 'fault' in case of a soap fault, and GetLastError becomes the faultcode
188
			status_log "Error authenticating : $faultcode - $faultstring" green
189
			if {$faultcode == "wsse:FailedAuthentication" } {
190
				set error 2
191
			} elseif {$faultcode == "psf:Redirect"} {
8250 by kakaroto
Fix SSO Authentication psf:Redirect faultcode. now we redirect and resend the soap request. Fixes authentication issues for @msn.com users.. thx Takeshi
192
				set xml  [$soap GetResponse]
193
				set url [GetXmlEntry $xml "S:Envelope:S:Fault:psf:redirectUrl"]
194
				status_log "SSO Authentication redirected to $url"
195
				$soap configure -url $url
196
				if {[catch {$soap SendSOAPRequest} ] } {
8879 by kakaroto
Add a TTL for the soap messages so you can't send the same soap request more than -ttl (default 5) times.. this avoids looping forever with the redirections...
197
					set error 3
198
				} else {
199
					return
200
				}
201
			} else {
8235 by kakaroto
change soap API.. GetStatus will be 'fault' in case of a soap fault, and GetLastError becomes the faultcode
202
				set error 1
203
			}
204
			if {[catch {eval $callbk [list $error]} result]} {
205
				bgerror $result
206
			}
207
		} else {
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
208
			$soap destroy
209
			set soap_req ""
8849 by kakaroto
correctly reset the soap_req to avoid the invalid command name errors
210
			if {[catch {eval $callbk [list 1]} result]} {
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
211
				bgerror $result
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
212
			}
213
		}
214
	}
215
	
216
	method Authenticate { callbk } {
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
217
		if {$soap_req != "" } {
7885 by kakaroto
SSO auth object should cancel SOAP request if it gets destroyed + Code cleaning, removed debug outputs
218
			$soap_req destroy
219
			set soap_req ""
220
		}
221
		set soap_req [SOAPRequest create %AUTO% \
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
222
				  -url "https://login.live.com/RST.srf" \
223
				  -xml [$self getSSOXml] \
224
				  -callback [list $self AuthenticateCallback $callbk]]
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
225
		$soap_req SendSOAPRequest
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
226
	}
227
	
228
	method getSSOXml { args } {
229
230
		set xml {<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc" xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">}
231
232
		append xml {<Header>}
233
		append xml {<ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">}
234
		append xml {<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>}
235
		append xml {<ps:BinaryVersion>4</ps:BinaryVersion>}
236
		append xml {<ps:UIVersion>1</ps:UIVersion>}
237
		append xml {<ps:Cookies></ps:Cookies>}
238
		append xml {<ps:RequestParams>AQAAAAIAAABsYwQAAAA0MTA1</ps:RequestParams>}
8460 by kakaroto
Fix the 'internal server error' users were having when using passwords with special chars and upper case letters
239
		append xml {</ps:AuthInfo>}
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
240
241
		append xml {<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext">}
242
		append xml {<wsse:UsernameToken Id="user">}
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
243
		append xml "<wsse:Username>[xmlencode $options(-username)]</wsse:Username>"
7926 by kakaroto
XML encode everything.. you never know where an '&' could get in the middle of our xml...
244
		append xml "<wsse:Password>[xmlencode $options(-password)]</wsse:Password>"
245
		append xml {</wsse:UsernameToken>}
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
246
		append xml {</wsse:Security>}
247
		append xml {</Header>}
248
		
249
		append xml {<Body>}
250
		append xml {<ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">}
251
252
		set id 0
253
		foreach token $security_tokens {
254
			set address [$token cget -address]
255
			set policy [$token cget -policy]
256
257
			append xml "<wst:RequestSecurityToken Id=\"RST${id}\">"
258
			append xml {<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>}
259
			append xml {<wsp:AppliesTo>}
260
			append xml {<wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">}
261
			append xml "<wsa:Address>${address}</wsa:Address>"
262
			append xml {</wsa:EndpointReference>}
263
			append xml {</wsp:AppliesTo>}
264
265
			# The http://Passport.NET/tb token doesn't have a policy reference
266
			if {$policy != ""} {
267
				append xml "<wsse:PolicyReference xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"[xmlencode ${policy}]\">"
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
268
				append xml {</wsse:PolicyReference>}
7867 by kakaroto
Fixes the issues i had before.. now SSO authentication works 100%svn diff yeay! :)
269
			}
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
270
			append xml {</wst:RequestSecurityToken>}
271
			
272
			incr id
273
		}
274
275
		append xml {</ps:RequestMultipleSecurityTokens>}
276
		append xml {</Body>}
277
		append xml {</Envelope>}
278
279
		return $xml
280
	}
281
}
282
283
284
snit::type MBIAuthentication {
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
285
286
	typemethod MBICrypt {nonce proof } {
287
		set key1 [base64::decode $proof]
288
		set key2 [MBIAuthentication deriveKey $key1 "WS-SecureConversationSESSION KEY HASH"]
289
		set key3 [MBIAuthentication deriveKey $key1 "WS-SecureConversationSESSION KEY ENCRYPTION"]
290
291
		set hash [binary format H* [::sha1::hmac $key2 $nonce]]
292
293
		set iv [MBIAuthentication rand 8]
294
		set des_message $nonce
295
		append des_message [string repeat "\x08" [expr {72 - [string length $nonce]}]]
296
		set cipher [::DES::des -mode cbc -dir encrypt -key $key3 -iv $iv -- $des_message]
9546 by kakaroto
Add '--' arg to ::DES::des to avoid it crashing when the encrypted password starts with a dash
297
7869 by kakaroto
MSNP15 support!!! (partial + experimental). SSO authentication works, MBI cryptography works, we can authenticate and chat and read/send OIMs, etc... BUT we just don't download the contact list yet.. will be added soon! :)
298
299
		set header [binary format iiH8H8iii 28 1 03660000 04800000 [string length $iv] [string length $hash] [string length $cipher]]
300
		set data "${iv}${hash}${cipher}"
301
302
		return [string map {"\n" ""} [base64::encode "${header}${data}"]]
303
	}
304
305
	typemethod deriveKey { key magic } {
306
		set hash1 [binary format H* [::sha1::hmac $key $magic]]
307
		set hash2 [binary format H* [::sha1::hmac $key "${hash1}${magic}"]]
308
309
		set hash3 [binary format H* [::sha1::hmac $key $hash1]]
310
		set hash4 [binary format H* [::sha1::hmac $key "${hash3}${magic}"]]
311
		return "${hash2}[string range $hash4 0 3]"
312
	}
313
314
	typemethod rand { bytes } {
315
		set result ""
316
		for {set i 0 } { $i < $bytes } { incr i } {
317
			append result [binary format c [expr {int(rand() * 256)}]]
318
		}
319
		return $result
320
	}
321
}
322
323
snit::type LockKeyAuthentication {
7866 by kakaroto
Initial implementation of the SSO authentication algorithm. Not working for now, but I have to go, will fix once I get home
324
	
325
326
	method CreateLockKey {chldata prodid prodkey} {
327
        
328
                # Create an MD5 hash out of the given data, then form 32 bit integers from it
329
                set md5hash [::md5::md5 $chldata$prodkey]
330
                set md5parts [$self MD5HashToInt $md5hash]
331
        
332
333
                # Then create a valid productid string, divisable by 8, then form 32 bit integers from it
334
                set nrPadZeros [expr {8 - [string length $chldata$prodid] % 8}]
335
                set padZeros [string repeat 0 $nrPadZeros]
336
                set chlprodid [$self CHLProdToInt $chldata$prodid$padZeros]
337
338
                # Create the key we need to XOR
339
                set key [$self KeyFromInt $md5parts $chlprodid]
340
341
                set low 0x[string range $md5hash 0 15]
342
                set high 0x[string range $md5hash 16 32]
343
                set low [expr {$low ^ $key}]
344
                set high [expr {$high ^ $key}]
345
346
                set p1 [format %8.8x [expr {($low / 0x100000000) % 0x100000000}]]
347
                set p2 [format %8.8x [expr {$low % 0x100000000}]]
348
                set p3 [format %8.8x [expr {($high / 0x100000000) % 0x100000000}]]
349
                set p4 [format %8.8x [expr {$high % 0x100000000}]]
350
351
                return $p1$p2$p3$p4
352
        }
353
354
        method KeyFromInt { md5parts chlprod } {
355
                # Create a new series of numbers
356
                set key_temp 0
357
                set key_high 0
358
                set key_low 0
359
        
360
                # Then loop on the entries in the second array we got in the parameters
361
                for {set i 0} {$i < [llength $chlprod]} {incr i 2} {
362
363
                        # Make $key_temp zero again and perform calculation as described in the documents
364
                        set key_temp [lindex $chlprod $i]
365
                        set key_temp [expr {(wide(0x0E79A9C1) * wide($key_temp)) % wide(0x7FFFFFFF)}]
366
                        set key_temp [expr {wide($key_temp) + wide($key_high)}]
367
                        set key_temp [expr {(wide([lindex $md5parts 0]) * wide($key_temp)) + wide([lindex $md5parts 1])}]
368
                        set key_temp [expr {wide($key_temp) % wide(0x7FFFFFFF)}]
369
370
                        set key_high [lindex $chlprod [expr {$i+1}]]
371
                        set key_high [expr {(wide($key_high) + wide($key_temp)) % wide(0x7FFFFFFF)}]
372
                        set key_high [expr {(wide([lindex $md5parts 2]) * wide($key_high)) + wide([lindex $md5parts 3])}]
373
                        set key_high [expr {wide($key_high) % wide(0x7FFFFFFF)}]
374
375
                        set key_low [expr {wide($key_low) + wide($key_temp) + wide($key_high)}]
376
                }
377
378
                set key_high [expr {(wide($key_high) + wide([lindex $md5parts 1])) % wide(0x7FFFFFFF)}]
379
                set key_low [expr {(wide($key_low) + wide([lindex $md5parts 3])) % wide(0x7FFFFFFF)}]
380
381
                set key_high 0x[$self byteInvert [format %8.8X $key_high]]
382
                set key_low 0x[$self byteInvert [format %8.8X $key_low]]
383
384
                set long_key [expr {(wide($key_high) << 32) + wide($key_low)}]
385
386
                return $long_key
387
        }
388
389
        # Takes an CHLData + ProdID + Padded string and chops it in 4 bytes. Then converts to 32 bit integers 
390
        method CHLProdToInt { CHLProd } {
391
                set hexs {}
392
                set result {}
393
                while {[string length $CHLProd] > 0} {
394
                        lappend hexs [string range $CHLProd 0 3]
395
                        set CHLProd [string range $CHLProd 4 end]
396
                }
397
                for {set i 0} {$i < [llength $hexs]} {incr i} {
398
                        binary scan [lindex $hexs $i] H8 int
399
                        lappend result 0x[$self byteInvert $int]
400
                }
401
                return $result
402
        }
403
                
404
405
        # Takes an MD5 string and chops it in 4. Then "decodes" the HEX and converts to 32 bit integers. After that it ANDs
406
        method MD5HashToInt { md5hash } {
407
                binary scan $md5hash a8a8a8a8 hash1 hash2 hash3 hash4
408
                set hash1 [expr {"0x[$self byteInvert $hash1]" & 0x7FFFFFFF}]
409
                set hash2 [expr {"0x[$self byteInvert $hash2]" & 0x7FFFFFFF}]
410
                set hash3 [expr {"0x[$self byteInvert $hash3]" & 0x7FFFFFFF}]
411
                set hash4 [expr {"0x[$self byteInvert $hash4]" & 0x7FFFFFFF}]
412
                
413
                return [list $hash1 $hash2 $hash3 $hash4]
414
        }
415
416
        method byteInvert { hex } {
417
                set hexs {}
418
                while {[string length $hex] > 0} {
419
                        lappend hexs [string range $hex 0 1]
420
                        set hex [string range $hex 2 end]
421
                }
422
                set hex ""
423
                for {set i [expr [llength $hexs] -1]} {$i >= 0} {incr i -1} {
424
                        append hex [lindex $hexs $i]
425
                }
426
                return $hex
427
        }
428
429
}
430
431