Example #1
0
class BAASClient:
	def __init__(self):
		self.client = HTTPClient()
		
		self.url = "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com"
		self.user_agent = "libcurl (nnAccount; 789f928b-138e-4b2f-afeb-1acae821d897; SDK 9.3.0.0; Add-on 9.3.0.0)"
		self.power_state = "FA"
		
		self.access_token = None
		self.login_token = None
		
	def set_url(self, url): self.url = url
	def set_user_agent(self, agent): self.user_agent = agent
	def set_power_state(self, state): self.power_state = state
		
	def request(self, req, token, use_power_state):
		req.headers["Host"] = self.url
		req.headers["User-Agent"] = self.user_agent
		req.headers["Accept"] = "*/*"
		if token:
			req.headers["Authorization"] = token
		if use_power_state:
			req.headers["X-Nintendo-PowerState"] = self.power_state
		req.headers["Content-Length"] = 0
		req.headers["Content-Type"] = "application/x-www-form-urlencoded"
		
		response = self.client.request(req, True)
		if response.status not in [200, 201]:
			logger.warning("BAAS request returned error: %s" %response.json)
			raise BAASError("BAAS request failed: %s" %response.json["title"])
		return response
		
	def authenticate(self, device_token):
		req = HTTPRequest.post("/1.0.0/application/token")
		req.form["grantType"] = "public_client"
		req.form["assertion"] = device_token
		
		response = self.request(req, False, True)
		self.access_token = response.json["tokenType"] + " " + response.json["accessToken"]
		return response.json
		
	def login(self, id, password, app_token=None):
		req = HTTPRequest.post("/1.0.0/login")
		req.form["id"] = id
		req.form["password"] = password
		if app_token:
			req.form["appAuthNToken"] = app_token
			
		response = self.request(req, self.access_token, True)
		self.login_token = response.json["tokenType"] + " " + response.json["accessToken"]
		return response.json
		
	def register(self):
		req = HTTPRequest.post("/1.0.0/users")
		response = self.request(req, self.access_token, False)
		return response.json
Example #2
0
class BAASClient:
    def __init__(self):
        self.client = HTTPClient()

        self.url = "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com"
        self.user_agent = USER_AGENT[LATEST_VERSION]
        self.power_state = "FA"

        self.access_token = None
        self.login_token = None

    def set_url(self, url):
        self.url = url

    def set_user_agent(self, agent):
        self.user_agent = agent

    def set_power_state(self, state):
        self.power_state = state

    def set_system_version(self, version):
        if version not in USER_AGENT:
            raise ValueError("Unknown system version")
        self.user_agent = USER_AGENT[version]

    def request(self, req, token, use_power_state):
        req.headers["Host"] = self.url
        req.headers["User-Agent"] = self.user_agent
        req.headers["Accept"] = "*/*"
        if token:
            req.headers["Authorization"] = token
        if use_power_state:
            req.headers["X-Nintendo-PowerState"] = self.power_state
        req.headers["Content-Length"] = 0
        req.headers["Content-Type"] = "application/x-www-form-urlencoded"

        response = self.client.request(req, True)
        if response.status not in [200, 201]:
            logger.warning("BAAS request returned error: %s" % response.json)
            raise BAASError(response.json["title"])
        return response

    def authenticate(self, device_token):
        req = HTTPRequest.post("/1.0.0/application/token")
        req.form["grantType"] = "public_client"
        req.form["assertion"] = device_token

        response = self.request(req, None, True)
        self.access_token = response.json["tokenType"] + " " + response.json[
            "accessToken"]
        return response.json

    def login(self, id, password, app_token=None):
        req = HTTPRequest.post("/1.0.0/login")
        req.form["id"] = "%016x" % id
        req.form["password"] = password
        if app_token:
            req.form["appAuthNToken"] = app_token

        response = self.request(req, self.access_token, True)
        self.login_token = response.json["tokenType"] + " " + response.json[
            "accessToken"]
        return response.json

    def register(self):
        req = HTTPRequest.post("/1.0.0/users")
        response = self.request(req, self.access_token, False)
        return response.json
Example #3
0
class DAuthClient:
    def __init__(self, keyset):
        self.client = HTTPClient()
        self.keyset = keyset

        self.cert = None
        self.region = 1

        self.client_id = 0x8F849B5D34778D8E

        self.url = "dauth-lp1.ndas.srv.nintendo.net"
        self.user_agent = "libcurl (nnDauth; 16f4553f-9eee-4e39-9b61-59bc7c99b7c8; SDK 9.3.0.0)"
        self.system_digest = "CusHY#00090200#Uxxmc8gYnfMqxzdZdygZ_OrKo98O7QA65s_EkZnGsDo="

        self.power_state = "FA"

        self.key_generation = 11

    def set_certificate(self, cert, key):
        self.cert = cert, key

    def set_platform_region(self, region):
        self.region = region

    def set_client_id(self, id):
        self.client_id = id

    def set_url(self, url):
        self.url = url

    def set_user_agent(self, agent):
        self.user_agent = agent

    def set_system_digest(self, digest):
        self.system_digest = digest

    def set_power_state(self, state):
        self.power_state = state

    def set_key_generation(self, keygen):
        self.key_generation = keygen

    def request(self, req):
        req.certificate = self.cert

        req.headers["Host"] = self.url
        req.headers["User-Agent"] = self.user_agent
        req.headers["Accept"] = "*/*"
        req.headers["X-Nintendo-PowerState"] = self.power_state
        req.headers["Content-Length"] = 0
        req.headers["Content-Type"] = "application/x-www-form-urlencoded"

        response = self.client.request(req, True)
        if response.status != 200:
            if response.json is not None:
                logger.error("DAuth request returned errors:")
                for error in response.json["errors"]:
                    logger.error("  (%s) %s", error["code"], error["message"])
                raise DAuthError("DAuth request failed: %s" %
                                 response.json["errors"][0]["message"])
            else:
                logger.error("DAuth request returned status code %i",
                             response.status)
                raise DAuthError("DAuth request failed with status %i" %
                                 response.status)
        return response

    def challenge(self):
        req = HTTPRequest.post("/v6/challenge")
        req.form["key_generation"] = self.key_generation

        response = self.request(req)
        return response.json

    def device_token(self):
        challenge = self.challenge()

        data = b64decode(challenge["data"])

        req = HTTPRequest.post("/v6/device_auth_token")
        req.form["challenge"] = challenge["challenge"]
        req.form["client_id"] = "%016x" % self.client_id
        if self.region == 2:
            req.form["ist"] = "true"
        else:
            req.form["ist"] = "false"
        req.form["key_generation"] = self.key_generation
        req.form["system_version"] = self.system_digest
        req.form["mac"] = self.calculate_mac(req.form.encode(), data)

        response = self.request(req)
        return response.json

    def get_master_key(self):
        keygen = self.key_generation
        keyname = "master_key_%02x" % (keygen - 1)
        return self.keyset.get(keyname)

    def decrypt_key(self, key, kek):
        aes = AES.new(kek, AES.MODE_ECB)
        return aes.decrypt(key)

    def calculate_mac(self, form, data):
        kek_source = self.keyset.get("aes_kek_generation_source")
        master_key = self.get_master_key()

        key = self.decrypt_key(kek_source, master_key)
        key = self.decrypt_key(DAUTH_SOURCE, key)
        key = self.decrypt_key(data, key)

        mac = CMAC.new(key, ciphermod=AES)
        mac.update(form.encode())
        return b64encode(mac.digest())
Example #4
0
class AAuthClient:
	def __init__(self):
		self.client = HTTPClient()
		
		self.url = "aauth-lp1.ndas.srv.nintendo.net"
		
		self.user_agent = "libcurl (nnAccount; 789f928b-138e-4b2f-afeb-1acae821d897; SDK 9.3.0.0; Add-on 9.3.0.0)"
		self.power_state = "FA"
	
	def set_url(self, url): self.url = url
	def set_user_agent(self, agent): self.user_agent = agent
	def set_power_state(self, state): self.power_state = state
	
	def request(self, req, use_power_state):
		req.headers["Host"] = self.url
		req.headers["User-Agent"] = self.user_agent
		req.headers["Accept"] = "*/*"
		if use_power_state:
			req.headers["X-Nintendo-PowerState"] = self.power_state
		req.headers["Content-Length"] = 0
		req.headers["Content-Type"] = "application/x-www-form-urlencoded"
		
		response = self.client.request(req, True)
		if response.status != 200:
			if response.json is not None:
				logger.error("AAuth request returned errors:")
				for error in response.json["errors"]:
					logger.error("  (%s) %s", error["code"], error["message"])
				raise AAuthError("AAuth request failed: %s" %response.json["errors"][0]["message"])
			else:
				logger.error("DAuth request returned status code %i", response.status)
				raise AAuthError("AAuth request failed with status %i" %response.status)
		return response
		
	def verify_ticket(self, ticket, title_id):
		if len(ticket) != 0x2C0:
			raise ValueError("Ticket has unexpected size")
		if struct.unpack_from("<I", ticket)[0] != 0x10004:
			raise ValueError("Ticket has invalid signature type")
		if struct.unpack_from(">Q", ticket, 0x2A0)[0] != title_id:
			raise ValueError("Ticket has different title id")
		if struct.unpack_from(">Q", ticket, 0x2A8)[0] != ticket[0x285]:
			raise ValueError("Ticket has inconsistent master key revision")
		
	def auth_digital(self, title_id, title_version, device_token, ticket):
		self.verify_ticket(ticket, title_id)
		
		plain_key = get_random_bytes(16)
		
		aes = AES.new(plain_key, AES.MODE_CBC, iv=bytes(16))
		encrypted_ticket = aes.encrypt(pad(ticket, 16))
		
		rsa_key = RSA.construct((RSA_MODULUS, RSA_EXPONENT))
		rsa = PKCS1_OAEP.new(rsa_key, SHA256)
		encrypted_key = rsa.encrypt(plain_key)
	
		req = HTTPRequest.post("/v3/application_auth_token")
		req.form["application_id"] = "%016x" %title_id
		req.form["application_version"] = "%08x" %title_version
		req.form["device_auth_token"] = device_token
		req.form["media_type"] = "DIGITAL"
		
		req.form["cert"] = b64encode(encrypted_ticket)
		req.form["cert_key"] = b64encode(encrypted_key)
	
		response = self.request(req, True)
		return response.json
Example #5
0
class HppClient(service.RMCClientBase):
    def __init__(self, settings):
        super().__init__(settings)
        self.game_server_id = None
        self.access_key = None
        self.nex_version = None

        self.pid = None
        self.password = None

        self.environment = "L1"

        self.key_derivation = kerberos.KeyDerivationOld(65000, 1024)

        self.client = HTTPClient()
        self.call_id = 0

    def set_environment(self, env):
        self.environment = env

    def configure(self, game_server_id, access_key, nex_version):
        self.game_server_id = game_server_id
        self.access_key = access_key
        self.nex_version = nex_version

        self.settings.set("nex.version", 20000)

    def login(self, pid, password):
        self.pid = pid
        self.password = password

    def host(self):
        return "hpp-%08x-%s.n.app.nintendo.net" % (self.game_server_id,
                                                   self.environment.lower())

    def send_request(self, protocol, method, body):
        call_id = self.call_id
        self.call_id += 1

        message = service.RMCMessage.request(self.settings, protocol, method,
                                             call_id, body)

        data = message.encode()

        key1 = bytes.fromhex(self.access_key).ljust(8, b"\0")
        key2 = self.key_derivation.derive_key(self.password.encode(), self.pid)

        signature1 = hmac.new(key1, data, hashlib.md5).hexdigest()
        signature2 = hmac.new(key2, data, hashlib.md5).hexdigest()

        random = secrets.token_hex(8).upper()

        req = HTTPRequest.post("https://%s/hpp/" % self.host())
        req.headers["Host"] = self.host()
        req.headers["pid"] = str(self.pid)
        req.headers["version"] = self.nex_version
        req.headers["token"] = "normaltoken"
        req.headers["signature1"] = signature1.upper()
        req.headers["signature2"] = signature2.upper()
        req.headers["Content-Type"] = "multipart/form-data"
        req.headers["Content-Length"] = 0
        req.boundary = "--------BOUNDARY--------" + random
        req.files["file"] = data

        response = self.client.request(req, True)
        if response.status != 200:
            raise ValueError("Hpp request failed with status %i" %
                             response.status)

        stream = streams.StreamIn(response.body, self.settings)
        if stream.u32() != stream.available():
            raise ValueError("Hpp response has unexpected size")

        success = stream.bool()
        if not success:
            error = stream.u32()
            if call_id != stream.u32():
                raise ValueError("Hpp error response has unexpected call id")
            if not stream.eof():
                raise ValueError("Hpp error response is bigger than expected")
            raise common.RMCError(error)

        if call_id != stream.u32():
            raise ValueError("Hpp response has unexpected call id")
        method_id = stream.u32()
        if method_id != method | 0x8000:
            raise ValueError("Hpp response has unexpected method id")
        return stream.readall()
Example #6
0
class DAuthClient:
	def __init__(self, keyset):
		self.client = HTTPClient()
		self.keyset = keyset
		
		self.cert = None
		self.region = 1
		
		self.client_id = 0x8F849B5D34778D8E
		
		self.url = "dauth-lp1.ndas.srv.nintendo.net"
		self.user_agent = USER_AGENT[LATEST_VERSION]
		self.system_digest = SYSTEM_VERSION_DIGEST[LATEST_VERSION]
		self.key_generation = KEY_GENERATION[LATEST_VERSION]
		
		self.power_state = "FA"
		
	def set_certificate(self, cert, key): self.cert = cert, key
	
	def set_platform_region(self, region): self.region = region
	def set_client_id(self, id): self.client_id = id
	
	def set_url(self, url): self.url = url
	def set_user_agent(self, agent): self.user_agent = agent
	def set_system_digest(self, digest): self.system_digest = digest
	def set_system_version(self, version):
		if version not in USER_AGENT:
			raise ValueError("Unknown system version")
		self.user_agent = USER_AGENT[version]
		self.system_digest = SYSTEM_VERSION_DIGEST[version]
		self.key_generation = KEY_GENERATION[version]
	
	def set_power_state(self, state): self.power_state = state
	
	def set_key_generation(self, keygen): self.key_generation = keygen
		
	def request(self, req):
		req.certificate = self.cert
		
		req.headers["Host"] = self.url
		req.headers["User-Agent"] = self.user_agent
		req.headers["Accept"] = "*/*"
		req.headers["X-Nintendo-PowerState"] = self.power_state
		req.headers["Content-Length"] = 0
		req.headers["Content-Type"] = "application/x-www-form-urlencoded"
		
		response = self.client.request(req, True)
		if response.status != 200:
			if response.json is not None:
				logger.error("DAuth request returned errors:")
				errors = response.json["errors"]
				for error in errors:
					logger.error("  (%s) %s", error["code"], error["message"])
				raise DAuthError(status_code=response.status, errors=errors)
			else:
				logger.error("DAuth request returned status code %i", response.status)
				raise DAuthError(status_code=response.status)
		return response
		
	def challenge(self):
		req = HTTPRequest.post("/v6/challenge")
		req.form["key_generation"] = self.key_generation
		
		response = self.request(req)
		return response.json
		
	def device_token(self):
		challenge = self.challenge()
		
		data = b64decode(challenge["data"])
		
		req = HTTPRequest.post("/v6/device_auth_token")
		req.form["challenge"] = challenge["challenge"]
		req.form["client_id"] = "%016x" %self.client_id
		if self.region == 2:
			req.form["ist"] = "true"
		else:
			req.form["ist"] = "false"
		req.form["key_generation"] = self.key_generation
		req.form["system_version"] = self.system_digest
		req.form["mac"] = self.calculate_mac(req.form.encode(), data)
		
		response = self.request(req)
		return response.json
		
	def get_master_key(self):
		keygen = self.key_generation
		keyname = "master_key_%02x" %(keygen - 1)
		return self.keyset.get(keyname)
		
	def decrypt_key(self, key, kek):
		aes = AES.new(kek, AES.MODE_ECB)
		return aes.decrypt(key)
		
	def calculate_mac(self, form, data):
		kek_source = self.keyset.get("aes_kek_generation_source")
		master_key = self.get_master_key()
		
		key = self.decrypt_key(kek_source, master_key)
		key = self.decrypt_key(DAUTH_SOURCE, key)
		key = self.decrypt_key(data, key)
		
		mac = CMAC.new(key, ciphermod=AES)
		mac.update(form.encode())
		return b64encode(mac.digest())
Example #7
0
class NNASClient:
	def __init__(self):
		self.client = HTTPClient()
		
		cert = ssl.SSLCertificate.load(CERT, ssl.TYPE_PEM)
		key = ssl.SSLPrivateKey.load(KEY, ssl.TYPE_PEM)
		self.cert = cert, key
		
		self.url = "account.nintendo.net"
		
		self.client_id = "a2efa818a34fa16b8afbc8a74eba3eda"
		self.client_secret = "c91cdb5658bd4954ade78533a339cf9a"
		
		self.platform_id = 1
		self.device_type = 2
		
		self.device_id = None
		self.serial_number = None
		self.system_version = 0x250
		self.device_cert = None
		
		self.region = 4
		self.country = "NL"
		self.language = "en"
		
		self.fpd_version = 0
		self.environment = "L1"
		
		self.title_id = None
		self.title_version = None
		
		self.auth_token = None
		
	def set_certificate(self, cert, key): self.cert = cert, key
	
	def set_url(self, url): self.url = url
	
	def set_client_id(self, client_id): self.client_id = client_id
	def set_client_secret(self, client_secret): self.client_secret = client_secret
	
	def set_platform_id(self, platform_id): self.platform_id = platform_id
	def set_device_type(self, device_type): self.device_type = device_type
	
	def set_device(self, device_id, serial_number, system_version, cert=None):
		self.device_id = device_id
		self.serial_number = serial_number
		self.system_version = system_version
		self.device_cert = cert
		
	def set_locale(self, region, country, language):
		self.region = region
		self.country = country
		self.language = language
		
	def set_fpd_version(self, version): self.fpd_version = version
	def set_environment(self, environment): self.environment = environment
	
	def set_title(self, title_id, title_version):
		self.title_id = title_id
		self.title_version = title_version
	
	def prepare(self, req, auth=None, cert=None):
		req.certificate = self.cert
		
		req.headers["Host"] = self.url
		req.headers["X-Nintendo-Platform-ID"] = self.platform_id
		req.headers["X-Nintendo-Device-Type"] = self.device_type
		
		if self.device_id is not None:
			req.headers["X-Nintendo-Device-ID"] = self.device_id
		if self.serial_number is not None:
			req.headers["X-Nintendo-Serial-Number"] = self.serial_number
			
		req.headers["X-Nintendo-System-Version"] = "%04X" %self.system_version
		req.headers["X-Nintendo-Region"] = self.region
		req.headers["X-Nintendo-Country"] = self.country
		req.headers["Accept-Language"] = self.language
		
		if auth is None:
			req.headers["X-Nintendo-Client-ID"] = self.client_id
			req.headers["X-Nintendo-Client-Secret"] = self.client_secret
			
		req.headers["Accept"] = "*/*"
		req.headers["X-Nintendo-FPD-Version"] = "%04X" %self.fpd_version
		req.headers["X-Nintendo-Environment"] = self.environment
		
		if self.title_id is not None:
			req.headers["X-Nintendo-Title-ID"] = "%016X" %self.title_id
			req.headers["X-Nintendo-Unique-ID"] = "%05X" %((self.title_id >> 8) & 0xFFFFF)
		if self.title_version is not None:
			req.headers["X-Nintendo-Application-Version"] = "%04X" %self.title_version
			
		if cert is not None:
			req.headers["X-Nintendo-Device-Cert"] = cert
			
		if auth is not None:
			req.headers["Authorization"] = auth
			
	def request(self, req):
		response = self.client.request(req, True)
		if response.error():
			logger.error("Account request returned status code %i\n%s", response.status, response.text)
			raise NNASError("Account request failed with status %i" %response.status)
		return response.xml
		
	def login(self, username, password, password_type=None):
		req = HTTPRequest.post("/v1/api/oauth20/access_token/generate")
		self.prepare(req, cert=self.device_cert)
		
		req.form["grant_type"] = "password"
		req.form["user_id"] = util.urlencode(username)
		req.form["password"] = util.urlencode(password)
		if password_type is not None:
			req.form["password_type"] = password_type
		
		response = self.request(req)
		self.auth_token = "Bearer " + response["access_token"]["token"].value
		
	def get_emails(self):
		req = HTTPRequest.get("/v1/api/people/@me/emails")
		self.prepare(req, self.auth_token)
		return [Email.parse(email) for email in self.request(req)]
		
	def get_profile(self):
		req = HTTPRequest.get("/v1/api/people/@me/profile")
		self.prepare(req, self.auth_token)
		return Profile.parse(self.request(req))
		
	def get_nex_token(self, game_server_id):
		req = HTTPRequest.get("/v1/api/provider/nex_token/@me")
		req.params["game_server_id"] = "%08X" %game_server_id
		self.prepare(req, self.auth_token)
		return NexToken.parse(self.request(req))
		
	#The following functions can be used without logging in
		
	def get_miis(self, pids):
		req = HTTPRequest.get("/v1/api/miis")
		req.params["pids"] = util.urlencode(",".join([str(pid) for pid in pids]))
		self.prepare(req)
		
		response = self.request(req)
		return [Mii.parse(mii) for mii in response]
		
	def get_pids(self, nnids):
		req = HTTPRequest.get("/v1/api/admin/mapped_ids")
		req.params["input_type"] = "user_id"
		req.params["output_type"] = "pid"
		req.params["input"] = util.urlencode(",".join(nnids))
		self.prepare(req)
		
		response = self.request(req)
		return {id["in_id"].value: int(id["out_id"].value) for id in response}
		
	def get_nnids(self, pids):
		req = HTTPRequest.get("/v1/api/admin/mapped_ids")
		req.params["input_type"] = "pid"
		req.params["output_type"] = "user_id"
		req.params["input"] = util.urlencode(",".join([str(pid) for pid in pids]))
		self.prepare(req)
		
		response = self.request(req)
		return {int(id["in_id"].value): id["out_id"].value for id in response}
	
	def get_mii(self, pid): return self.get_miis([pid])[0]
	def get_pid(self, nnid): return self.get_pids([nnid])[nnid]
	def get_nnid(self, pid): return self.get_nnids([pid])[pid]