gs_utility.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. """DWC Network Server Emulator
  2. Copyright (C) 2014 polaris-
  3. Copyright (C) 2014 ToadKing
  4. Copyright (C) 2014 AdmiralCurtiss
  5. Copyright (C) 2014 msoucy
  6. Copyright (C) 2015 Sepalani
  7. This program is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU Affero General Public License as
  9. published by the Free Software Foundation, either version 3 of the
  10. License, or (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU Affero General Public License for more details.
  15. You should have received a copy of the GNU Affero General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. """
  18. import base64
  19. import hashlib
  20. import time
  21. import other.utils as utils
  22. def generate_secret_keys(filename="gslist.cfg"):
  23. """Generate list of secret keys based on a config file.
  24. gslist.cfg is the default config file and may be incomplete.
  25. TODO: Parse the config file in a cleaner way. (ex: using CSV module)
  26. """
  27. secret_key_list = {}
  28. with open(filename) as key_file:
  29. for line in key_file.readlines():
  30. # name = line[:54].strip()
  31. # Probably won't do anything with the name for now.
  32. id = line[54:54+19].strip()
  33. key = line[54+19:].strip()
  34. secret_key_list[id] = key
  35. return secret_key_list
  36. def base64_encode(input):
  37. """Encode input in base64 using GameSpy variant.
  38. GameSpy uses a slightly modified version of base64 which replaces
  39. +/= with []_
  40. """
  41. output = (base64.b64encode(input.encode())).decode('utf-8').replace('+', '[') \
  42. .replace('/', ']') \
  43. .replace('=', '_')
  44. return output
  45. def base64_decode(input):
  46. """Decode input in base64 using GameSpy variant."""
  47. output = base64.b64decode(input.replace('[', '+')
  48. .replace(']', '/')
  49. .replace('_', '='))
  50. return output
  51. def rc4_encrypt(_key, _data):
  52. """
  53. Tetris DS overlay 10 @ 0216E9B8
  54. """
  55. key = bytearray(_key)
  56. data = bytearray(_data)
  57. if len(key) == 0:
  58. # This shouldn't happen but it apparently can on a rare occasion.
  59. # Key should always be set.
  60. return
  61. # Key-scheduling algorithm
  62. S = range(0x100)
  63. j = 0
  64. for i in range(0x100):
  65. # Get index to swap with
  66. j = (j + S[i] + key[i % len(key)]) & 0xff
  67. # Perform swap
  68. S[i], S[j] = S[j], S[i]
  69. # Pseudo-random generation algorithm + encryption
  70. i = 0
  71. j = 0
  72. for x, val in enumerate(data):
  73. # Modified RC4?
  74. i = (i + 1 + val) & 0xff
  75. j = (j + S[i]) & 0xff
  76. S[i], S[j] = S[j], S[i]
  77. data[x] ^= S[(S[i] + S[j]) & 0xff]
  78. return data
  79. def prepare_rc4_base64(_key, _data):
  80. """Tetris DS overlay 10 @ 0216E9B8
  81. Used by the master server to send some data between the client and server.
  82. This seems to be what Luigi Auriemma calls "Gsmsalg".
  83. """
  84. data = rc4_encrypt(_key, _data)
  85. if data is None:
  86. data = bytearray()
  87. data.append(0)
  88. return base64.b64encode(buffer(data))
  89. def parse_authtoken(authtoken, db):
  90. """Get the login data from nas.nintendowifi.net/ac from an authtoken"""
  91. return db.get_nas_login(authtoken)
  92. def login_profile_via_parsed_authtoken(authtoken_parsed, db):
  93. """Return login profile via parsed authtoken.
  94. authtoken_parsed MUST HAVE userid field and can't be None!
  95. """
  96. if authtoken_parsed is None or 'userid' not in authtoken_parsed:
  97. return None, None, None, None
  98. console = 0
  99. userid = authtoken_parsed['userid']
  100. csnum = authtoken_parsed.get('csnum', '') # Wii: Serial number
  101. cfc = authtoken_parsed.get('cfc', '') # Wii: Friend code
  102. bssid = authtoken_parsed.get('bssid', '') # NDS: Wifi network's BSSID
  103. devname = authtoken_parsed.get('devname', '') # NDS: Device name
  104. birth = authtoken_parsed.get('birth', '') # NDS: User's birthday
  105. # The Wii does not use passwd, so take another uniquely generated string
  106. # as the password.
  107. # if "passwd" in authtoken_parsed:
  108. # password = authtoken_parsed['passwd']
  109. # else:
  110. # password = authtoken_parsed['gsbrcd']
  111. # console = 1
  112. if "passwd" not in authtoken_parsed:
  113. console = 1
  114. password = authtoken_parsed['gsbrcd']
  115. gsbrcd = authtoken_parsed['gsbrcd']
  116. gameid = gsbrcd[:4]
  117. macadr = authtoken_parsed['macadr']
  118. uniquenick = utils.base32_encode(int(userid)) + gsbrcd
  119. email = uniquenick + "@nds" # The Wii also seems to use @nds.
  120. if "csnum" in authtoken_parsed:
  121. console = 1
  122. if "cfc" in authtoken_parsed:
  123. console = 1
  124. valid_user = db.check_user_exists(userid, gsbrcd)
  125. if valid_user is False:
  126. profileid = db.create_user(userid, password, email, uniquenick,
  127. gsbrcd, console, csnum, cfc, bssid,
  128. devname, birth, gameid, macadr)
  129. else:
  130. profileid = db.perform_login(userid, password, gsbrcd)
  131. return userid, profileid, gsbrcd, uniquenick
  132. def generate_response(challenge, ac_challenge, secretkey, authtoken):
  133. """Generate a challenge response."""
  134. md5 = hashlib.md5()
  135. md5.update(ac_challenge)
  136. output = md5.hexdigest()
  137. output += ' ' * 0x30
  138. output += authtoken
  139. output += secretkey
  140. output += challenge
  141. output += md5.hexdigest()
  142. md5_2 = hashlib.md5()
  143. md5_2.update(output)
  144. return md5_2.hexdigest()
  145. def generate_proof(challenge, ac_challenge, secretkey, authtoken):
  146. """Generate a challenge proof.
  147. The proof is practically the same thing as the response, except it has
  148. the challenge and the secret key swapped.
  149. Maybe combine the two functions later?
  150. """
  151. md5 = hashlib.md5()
  152. md5.update(ac_challenge)
  153. output = md5.hexdigest()
  154. output += ' ' * 0x30
  155. output += authtoken
  156. output += challenge
  157. output += secretkey
  158. output += md5.hexdigest()
  159. md5_2 = hashlib.md5()
  160. md5_2.update(output)
  161. return md5_2.hexdigest()
  162. def get_friendcode_from_profileid(profileid, gameid):
  163. """
  164. Code: Tetris DS @ 02057A14
  165. """
  166. friendcode = 0
  167. # Combine the profileid and gameid into one buffer
  168. buffer = [(profileid >> (8 * i)) & 0xff for i in range(4)]
  169. buffer += [ord(c) for c in gameid]
  170. crc = utils.calculate_crc8(buffer)
  171. # The upper 32 bits is the crc8 of the combined buffer.
  172. # The lower 32 bits of the friend code is the profileid.
  173. friendcode = ((crc & 0x7f) << 32) | profileid
  174. return friendcode
  175. def get_profileid_from_friendcode(friendcode):
  176. """Return profile ID from Friend Code."""
  177. # Get the lower 32 bits as the profile id
  178. profileid = friendcode & 0xffffffff
  179. return profileid
  180. class EncTypeX:
  181. """Code from Luigi Auriemma's enctypex_decoder.c
  182. It's kind of sloppy in parts, but it works. Unless there's some issues
  183. then it'll probably not change any longer.
  184. """
  185. def __init__(self):
  186. return
  187. def decrypt(self, key, validate, data):
  188. if not key or not validate or not data:
  189. return None
  190. encxkey = bytearray([0] * 261)
  191. data = self.init(encxkey, key, validate, data)
  192. self.func6(encxkey, data, len(data))
  193. return data
  194. def encrypt(self, key, validate, data):
  195. if not key or not validate or not data:
  196. return None
  197. # Convert data from strings to byte arrays before use or else
  198. # it'll raise an error
  199. key = bytearray(key)
  200. validate = bytearray(validate)
  201. # Add room for the header
  202. tmp_len = 20
  203. data = bytearray(tmp_len) + data
  204. keylen = len(key)
  205. vallen = len(validate)
  206. rnd = ~int(time.time())
  207. for i in range(tmp_len):
  208. rnd = (rnd * 0x343FD) + 0x269EC3
  209. data[i] = (rnd ^ key[i % keylen] ^ validate[i % vallen]) & 0xff
  210. header_len = 7
  211. data[0] = (header_len - 2) ^ 0xec
  212. data[1] = 0x00
  213. data[2] = 0x00
  214. data[header_len - 1] = (tmp_len - header_len) ^ 0xea
  215. # The header of the data gets chopped off in init(), so save it
  216. header = data[:tmp_len]
  217. encxkey = bytearray([0] * 261)
  218. data = self.init(encxkey, key, validate, data)
  219. self.func6e(encxkey, data, len(data))
  220. # Reappend header that we saved earlier before returning to make
  221. # the complete buffer
  222. return header + data
  223. def init(self, encxkey, key, validate, data):
  224. data_len = len(data)
  225. if data_len < 1:
  226. return None
  227. header_len = (data[0] ^ 0xec) + 2
  228. if data_len < header_len:
  229. return None
  230. data_start = (data[header_len - 1] ^ 0xea)
  231. if data_len < (header_len + data_start):
  232. return None
  233. data = self.enctypex_funcx(
  234. encxkey,
  235. bytearray(key),
  236. bytearray(validate),
  237. data[header_len:],
  238. data_start
  239. )
  240. return data[data_start:]
  241. def enctypex_funcx(self, encxkey, key, validate, data, datalen):
  242. keylen = len(key)
  243. for i in range(datalen):
  244. validate[(key[i % keylen] * i) & 7] ^= validate[i & 7] ^ data[i]
  245. self.func4(encxkey, validate, 8)
  246. return data
  247. def func4(self, encxkey, id, idlen):
  248. if idlen < 1:
  249. return
  250. for i in range(256):
  251. encxkey[i] = i
  252. n1 = 0
  253. n2 = 0
  254. for i in range(255, -1, -1):
  255. t1, n1, n2 = self.func5(encxkey, i, id, idlen, n1, n2)
  256. t2 = encxkey[i]
  257. encxkey[i] = encxkey[t1]
  258. encxkey[t1] = t2
  259. encxkey[256] = encxkey[1]
  260. encxkey[257] = encxkey[3]
  261. encxkey[258] = encxkey[5]
  262. encxkey[259] = encxkey[7]
  263. encxkey[260] = encxkey[n1 & 0xff]
  264. def func5(self, encxkey, cnt, id, idlen, n1, n2):
  265. if cnt == 0:
  266. return 0, n1, n2
  267. mask = 1
  268. doLoop = True
  269. if cnt > 1:
  270. while doLoop:
  271. mask = (mask << 1) + 1
  272. doLoop = mask < cnt
  273. i = 0
  274. tmp = 0
  275. doLoop = True
  276. while doLoop:
  277. n1 = encxkey[n1 & 0xff] + id[n2]
  278. n2 += 1
  279. if n2 >= idlen:
  280. n2 = 0
  281. n1 += idlen
  282. tmp = n1 & mask
  283. i += 1
  284. if i > 11:
  285. tmp %= cnt
  286. doLoop = tmp > cnt
  287. return tmp, n1, n2
  288. def func6(self, encxkey, data, data_len):
  289. for i in range(data_len):
  290. data[i] = self.func7(encxkey, data[i])
  291. return len(data)
  292. def func7(self, encxkey, d):
  293. a = encxkey[256]
  294. b = encxkey[257]
  295. c = encxkey[a]
  296. encxkey[256] = (a + 1) & 0xff
  297. encxkey[257] = (b + c) & 0xff
  298. a = encxkey[260]
  299. b = encxkey[257]
  300. b = encxkey[b]
  301. c = encxkey[a]
  302. encxkey[a] = b
  303. a = encxkey[259]
  304. b = encxkey[257]
  305. a = encxkey[a]
  306. encxkey[b] = a
  307. a = encxkey[256]
  308. b = encxkey[259]
  309. a = encxkey[a]
  310. encxkey[b] = a
  311. a = encxkey[256]
  312. encxkey[a] = c
  313. b = encxkey[258]
  314. a = encxkey[c]
  315. c = encxkey[259]
  316. b = (a + b) & 0xff
  317. encxkey[258] = b
  318. a = b
  319. c = encxkey[c]
  320. b = encxkey[257]
  321. b = encxkey[b]
  322. a = encxkey[a]
  323. c = (b + c) & 0xff
  324. b = encxkey[260]
  325. b = encxkey[b]
  326. c = (b + c) & 0xff
  327. b = encxkey[c]
  328. c = encxkey[256]
  329. c = encxkey[c]
  330. a = (a + c) & 0xff
  331. c = encxkey[b]
  332. b = encxkey[a]
  333. encxkey[260] = d
  334. c ^= b ^ d
  335. encxkey[259] = c
  336. return c
  337. def func6e(self, encxkey, data, data_len):
  338. for i in range(data_len):
  339. data[i] = self.func7e(encxkey, data[i])
  340. return len(data)
  341. def func7e(self, encxkey, d):
  342. a = encxkey[256]
  343. b = encxkey[257]
  344. c = encxkey[a]
  345. encxkey[256] = (a + 1) & 0xff
  346. encxkey[257] = (b + c) & 0xff
  347. a = encxkey[260]
  348. b = encxkey[257]
  349. b = encxkey[b]
  350. c = encxkey[a]
  351. encxkey[a] = b
  352. a = encxkey[259]
  353. b = encxkey[257]
  354. a = encxkey[a]
  355. encxkey[b] = a
  356. a = encxkey[256]
  357. b = encxkey[259]
  358. a = encxkey[a]
  359. encxkey[b] = a
  360. a = encxkey[256]
  361. encxkey[a] = c
  362. b = encxkey[258]
  363. a = encxkey[c]
  364. c = encxkey[259]
  365. b = (a + b) & 0xff
  366. encxkey[258] = b
  367. a = b
  368. c = encxkey[c]
  369. b = encxkey[257]
  370. b = encxkey[b]
  371. a = encxkey[a]
  372. c = (b + c) & 0xff
  373. b = encxkey[260]
  374. b = encxkey[b]
  375. c = (b + c) & 0xff
  376. b = encxkey[c]
  377. c = encxkey[256]
  378. c = encxkey[c]
  379. a = (a + c) & 0xff
  380. c = encxkey[b]
  381. b = encxkey[a]
  382. c ^= b ^ d
  383. encxkey[260] = c
  384. encxkey[259] = d
  385. return c