123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- """DWC Network Server Emulator
- Copyright (C) 2014 polaris-
- Copyright (C) 2014 ToadKing
- Copyright (C) 2014 AdmiralCurtiss
- Copyright (C) 2014 msoucy
- Copyright (C) 2015 Sepalani
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- import base64
- import hashlib
- import time
- import other.utils as utils
- def generate_secret_keys(filename="gslist.cfg"):
- """Generate list of secret keys based on a config file.
- gslist.cfg is the default config file and may be incomplete.
- TODO: Parse the config file in a cleaner way. (ex: using CSV module)
- """
- secret_key_list = {}
- with open(filename) as key_file:
- for line in key_file.readlines():
- # name = line[:54].strip()
- # Probably won't do anything with the name for now.
- id = line[54:54+19].strip()
- key = line[54+19:].strip()
- secret_key_list[id] = key
- return secret_key_list
- def base64_encode(input):
- """Encode input in base64 using GameSpy variant.
- GameSpy uses a slightly modified version of base64 which replaces
- +/= with []_
- """
- output = (base64.b64encode(input.encode())).decode('utf-8').replace('+', '[') \
- .replace('/', ']') \
- .replace('=', '_')
- return output
- def base64_decode(input):
- """Decode input in base64 using GameSpy variant."""
- output = base64.b64decode(input.replace('[', '+')
- .replace(']', '/')
- .replace('_', '='))
- return output
- def rc4_encrypt(_key, _data):
- """
- Tetris DS overlay 10 @ 0216E9B8
- """
- key = bytearray(_key)
- data = bytearray(_data)
- if len(key) == 0:
- # This shouldn't happen but it apparently can on a rare occasion.
- # Key should always be set.
- return
- # Key-scheduling algorithm
- S = range(0x100)
- j = 0
- for i in range(0x100):
- # Get index to swap with
- j = (j + S[i] + key[i % len(key)]) & 0xff
- # Perform swap
- S[i], S[j] = S[j], S[i]
- # Pseudo-random generation algorithm + encryption
- i = 0
- j = 0
- for x, val in enumerate(data):
- # Modified RC4?
- i = (i + 1 + val) & 0xff
- j = (j + S[i]) & 0xff
- S[i], S[j] = S[j], S[i]
- data[x] ^= S[(S[i] + S[j]) & 0xff]
- return data
- def prepare_rc4_base64(_key, _data):
- """Tetris DS overlay 10 @ 0216E9B8
- Used by the master server to send some data between the client and server.
- This seems to be what Luigi Auriemma calls "Gsmsalg".
- """
- data = rc4_encrypt(_key, _data)
- if data is None:
- data = bytearray()
- data.append(0)
- return base64.b64encode(buffer(data))
- def parse_authtoken(authtoken, db):
- """Get the login data from nas.nintendowifi.net/ac from an authtoken"""
- return db.get_nas_login(authtoken)
- def login_profile_via_parsed_authtoken(authtoken_parsed, db):
- """Return login profile via parsed authtoken.
- authtoken_parsed MUST HAVE userid field and can't be None!
- """
- if authtoken_parsed is None or 'userid' not in authtoken_parsed:
- return None, None, None, None
- console = 0
- userid = authtoken_parsed['userid']
- csnum = authtoken_parsed.get('csnum', '') # Wii: Serial number
- cfc = authtoken_parsed.get('cfc', '') # Wii: Friend code
- bssid = authtoken_parsed.get('bssid', '') # NDS: Wifi network's BSSID
- devname = authtoken_parsed.get('devname', '') # NDS: Device name
- birth = authtoken_parsed.get('birth', '') # NDS: User's birthday
- # The Wii does not use passwd, so take another uniquely generated string
- # as the password.
- # if "passwd" in authtoken_parsed:
- # password = authtoken_parsed['passwd']
- # else:
- # password = authtoken_parsed['gsbrcd']
- # console = 1
- if "passwd" not in authtoken_parsed:
- console = 1
- password = authtoken_parsed['gsbrcd']
- gsbrcd = authtoken_parsed['gsbrcd']
- gameid = gsbrcd[:4]
- macadr = authtoken_parsed['macadr']
- uniquenick = utils.base32_encode(int(userid)) + gsbrcd
- email = uniquenick + "@nds" # The Wii also seems to use @nds.
- if "csnum" in authtoken_parsed:
- console = 1
- if "cfc" in authtoken_parsed:
- console = 1
- valid_user = db.check_user_exists(userid, gsbrcd)
- if valid_user is False:
- profileid = db.create_user(userid, password, email, uniquenick,
- gsbrcd, console, csnum, cfc, bssid,
- devname, birth, gameid, macadr)
- else:
- profileid = db.perform_login(userid, password, gsbrcd)
- return userid, profileid, gsbrcd, uniquenick
- def generate_response(challenge, ac_challenge, secretkey, authtoken):
- """Generate a challenge response."""
- md5 = hashlib.md5()
- md5.update(ac_challenge)
- output = md5.hexdigest()
- output += ' ' * 0x30
- output += authtoken
- output += secretkey
- output += challenge
- output += md5.hexdigest()
- md5_2 = hashlib.md5()
- md5_2.update(output)
- return md5_2.hexdigest()
- def generate_proof(challenge, ac_challenge, secretkey, authtoken):
- """Generate a challenge proof.
- The proof is practically the same thing as the response, except it has
- the challenge and the secret key swapped.
- Maybe combine the two functions later?
- """
- md5 = hashlib.md5()
- md5.update(ac_challenge)
- output = md5.hexdigest()
- output += ' ' * 0x30
- output += authtoken
- output += challenge
- output += secretkey
- output += md5.hexdigest()
- md5_2 = hashlib.md5()
- md5_2.update(output)
- return md5_2.hexdigest()
- def get_friendcode_from_profileid(profileid, gameid):
- """
- Code: Tetris DS @ 02057A14
- """
- friendcode = 0
- # Combine the profileid and gameid into one buffer
- buffer = [(profileid >> (8 * i)) & 0xff for i in range(4)]
- buffer += [ord(c) for c in gameid]
- crc = utils.calculate_crc8(buffer)
- # The upper 32 bits is the crc8 of the combined buffer.
- # The lower 32 bits of the friend code is the profileid.
- friendcode = ((crc & 0x7f) << 32) | profileid
- return friendcode
- def get_profileid_from_friendcode(friendcode):
- """Return profile ID from Friend Code."""
- # Get the lower 32 bits as the profile id
- profileid = friendcode & 0xffffffff
- return profileid
- class EncTypeX:
- """Code from Luigi Auriemma's enctypex_decoder.c
- It's kind of sloppy in parts, but it works. Unless there's some issues
- then it'll probably not change any longer.
- """
- def __init__(self):
- return
- def decrypt(self, key, validate, data):
- if not key or not validate or not data:
- return None
- encxkey = bytearray([0] * 261)
- data = self.init(encxkey, key, validate, data)
- self.func6(encxkey, data, len(data))
- return data
- def encrypt(self, key, validate, data):
- if not key or not validate or not data:
- return None
- # Convert data from strings to byte arrays before use or else
- # it'll raise an error
- key = bytearray(key)
- validate = bytearray(validate)
- # Add room for the header
- tmp_len = 20
- data = bytearray(tmp_len) + data
- keylen = len(key)
- vallen = len(validate)
- rnd = ~int(time.time())
- for i in range(tmp_len):
- rnd = (rnd * 0x343FD) + 0x269EC3
- data[i] = (rnd ^ key[i % keylen] ^ validate[i % vallen]) & 0xff
- header_len = 7
- data[0] = (header_len - 2) ^ 0xec
- data[1] = 0x00
- data[2] = 0x00
- data[header_len - 1] = (tmp_len - header_len) ^ 0xea
- # The header of the data gets chopped off in init(), so save it
- header = data[:tmp_len]
- encxkey = bytearray([0] * 261)
- data = self.init(encxkey, key, validate, data)
- self.func6e(encxkey, data, len(data))
- # Reappend header that we saved earlier before returning to make
- # the complete buffer
- return header + data
- def init(self, encxkey, key, validate, data):
- data_len = len(data)
- if data_len < 1:
- return None
- header_len = (data[0] ^ 0xec) + 2
- if data_len < header_len:
- return None
- data_start = (data[header_len - 1] ^ 0xea)
- if data_len < (header_len + data_start):
- return None
- data = self.enctypex_funcx(
- encxkey,
- bytearray(key),
- bytearray(validate),
- data[header_len:],
- data_start
- )
- return data[data_start:]
- def enctypex_funcx(self, encxkey, key, validate, data, datalen):
- keylen = len(key)
- for i in range(datalen):
- validate[(key[i % keylen] * i) & 7] ^= validate[i & 7] ^ data[i]
- self.func4(encxkey, validate, 8)
- return data
- def func4(self, encxkey, id, idlen):
- if idlen < 1:
- return
- for i in range(256):
- encxkey[i] = i
- n1 = 0
- n2 = 0
- for i in range(255, -1, -1):
- t1, n1, n2 = self.func5(encxkey, i, id, idlen, n1, n2)
- t2 = encxkey[i]
- encxkey[i] = encxkey[t1]
- encxkey[t1] = t2
- encxkey[256] = encxkey[1]
- encxkey[257] = encxkey[3]
- encxkey[258] = encxkey[5]
- encxkey[259] = encxkey[7]
- encxkey[260] = encxkey[n1 & 0xff]
- def func5(self, encxkey, cnt, id, idlen, n1, n2):
- if cnt == 0:
- return 0, n1, n2
- mask = 1
- doLoop = True
- if cnt > 1:
- while doLoop:
- mask = (mask << 1) + 1
- doLoop = mask < cnt
- i = 0
- tmp = 0
- doLoop = True
- while doLoop:
- n1 = encxkey[n1 & 0xff] + id[n2]
- n2 += 1
- if n2 >= idlen:
- n2 = 0
- n1 += idlen
- tmp = n1 & mask
- i += 1
- if i > 11:
- tmp %= cnt
- doLoop = tmp > cnt
- return tmp, n1, n2
- def func6(self, encxkey, data, data_len):
- for i in range(data_len):
- data[i] = self.func7(encxkey, data[i])
- return len(data)
- def func7(self, encxkey, d):
- a = encxkey[256]
- b = encxkey[257]
- c = encxkey[a]
- encxkey[256] = (a + 1) & 0xff
- encxkey[257] = (b + c) & 0xff
- a = encxkey[260]
- b = encxkey[257]
- b = encxkey[b]
- c = encxkey[a]
- encxkey[a] = b
- a = encxkey[259]
- b = encxkey[257]
- a = encxkey[a]
- encxkey[b] = a
- a = encxkey[256]
- b = encxkey[259]
- a = encxkey[a]
- encxkey[b] = a
- a = encxkey[256]
- encxkey[a] = c
- b = encxkey[258]
- a = encxkey[c]
- c = encxkey[259]
- b = (a + b) & 0xff
- encxkey[258] = b
- a = b
- c = encxkey[c]
- b = encxkey[257]
- b = encxkey[b]
- a = encxkey[a]
- c = (b + c) & 0xff
- b = encxkey[260]
- b = encxkey[b]
- c = (b + c) & 0xff
- b = encxkey[c]
- c = encxkey[256]
- c = encxkey[c]
- a = (a + c) & 0xff
- c = encxkey[b]
- b = encxkey[a]
- encxkey[260] = d
- c ^= b ^ d
- encxkey[259] = c
- return c
- def func6e(self, encxkey, data, data_len):
- for i in range(data_len):
- data[i] = self.func7e(encxkey, data[i])
- return len(data)
- def func7e(self, encxkey, d):
- a = encxkey[256]
- b = encxkey[257]
- c = encxkey[a]
- encxkey[256] = (a + 1) & 0xff
- encxkey[257] = (b + c) & 0xff
- a = encxkey[260]
- b = encxkey[257]
- b = encxkey[b]
- c = encxkey[a]
- encxkey[a] = b
- a = encxkey[259]
- b = encxkey[257]
- a = encxkey[a]
- encxkey[b] = a
- a = encxkey[256]
- b = encxkey[259]
- a = encxkey[a]
- encxkey[b] = a
- a = encxkey[256]
- encxkey[a] = c
- b = encxkey[258]
- a = encxkey[c]
- c = encxkey[259]
- b = (a + b) & 0xff
- encxkey[258] = b
- a = b
- c = encxkey[c]
- b = encxkey[257]
- b = encxkey[b]
- a = encxkey[a]
- c = (b + c) & 0xff
- b = encxkey[260]
- b = encxkey[b]
- c = (b + c) & 0xff
- b = encxkey[c]
- c = encxkey[256]
- c = encxkey[c]
- a = (a + c) & 0xff
- c = encxkey[b]
- b = encxkey[a]
- c ^= b ^ d
- encxkey[260] = c
- encxkey[259] = d
- return c
|