UNPKG

node-pogo-signature

Version:

an 'unknown6' signature builder for pokemon go node apis - fully native js!

227 lines (191 loc) 7.98 kB
'use strict'; const protobuf = require('protobufjs'); const Utils = require('./utils'); const crypto = require('crypto'); const longjs = require('long'); const path = require('path'); const pcrypt = require("pcrypt"); let PROTO_Signature = protobuf.loadProtoFile(path.join(__dirname, 'proto', 'Signature.proto')).build().Signature; let PROTO_u6 = protobuf.loadProtoFile(path.join(__dirname, 'proto', 'Unknown6.proto')).build().Unknown6; /** * the signature builder * @constructor * @param {Object} [options] - a set of options and defaults to send to the signature builder * @param {number} [options[].initTime] - time in ms to use as the app's startup time * @param {Buffer} [options[].unk22] - a 32-byte Buffer to use as `unk22` * @param {String} [options[].version] - The version to run on, defaults to 0.45 */ let Builder = function(options) { if (!options) options = {} this.initTime = options.initTime || new Date().getTime(); this.time_since_start = options.time_since_start || null; this.time = options.time || null; this.version = options.version || "0.45"; if (options.protos) { PROTO_Signature = options.protos.Networking.Envelopes.Signature; } this.utils = new Utils(); this.fields = { session_hash: options.session_hash || options.unk22 || crypto.randomBytes(32) }; }; /** * sets the location to be used in signature building * @param {number} lat - latitude * @param {number} lng - longitude * @param {number} [accuracy=0] - accuracy */ Builder.prototype.setLocation = function(lat, lng, accuracy) { this.lat = lat; this.lng = lng; this.accuracy = accuracy || 0; } /** * sets the auth_ticket to be used in signature building * @param {Buffer|Object} authTicket - protobufjs constructor OR raw buffer containing bytes (must pass true for `isEncoded` when passing a Buffer) * @param {boolean} [isEncoded=false] - set to true if the authTicket is a protobuf encoded Buffer */ Builder.prototype.setAuthTicket = function(authTicket, isEncoded) { if (isEncoded) { this.authTicket = authTicket; } else { if (authTicket.encode) { this.authTicket = authTicket.encode().toBuffer(); } } } /** * merges a set of key-values into the internally stored fields for the signature * @param {Object} fields - key-value mapping for siganture fields */ Builder.prototype.setFields = function(fields) { for (let field in fields) { this.fields[field] = fields[field]; } } /** * Enables hashing server rather than native */ Builder.prototype.useHashingServer = function(url, key){ this.utils.useHashingServer(url, key); } /** * builds an unencrypted signature returned as a protobuf object or Buffer * @param {Object|Object[]|Buffer|Buffer[]} requests - array of RPC requests (protobuf objects or encoded protobuf Buffers) to be used in the signature generation * @returns {Promise} */ Builder.prototype.buildSignature = function(requests) { if (!Array.isArray(requests)) { requests = [requests]; } /// 0.45 supports both hashing server and native if(this.version === "0.45") { return new Promise((success, fail) => { this._hashFourtyFive(success, fail, requests); }); /// 0.51 supports only hashing server } else { return this._hashFifthOne(requests); } } /** * builds a signature given requests, and encrypts it afterwards * @global * @param {Object|Object[]|Buffer|Buffer[]} requests - array of RPC requests (protobuf objects or encoded protobuf Buffers) to be used in the signature generation * @param {Number} timestamp_since_start - the time since your request started, must match what you're sending in the signture * @param {encryptCallback} cb - function to be called when encryption is completed */ Builder.prototype.encrypt = function(requests, cb) { this.buildSignature(requests) .then(response => { this.utils.encrypt(response.encode().toBuffer(), +response.timestamp_since_start, this.version, cb); }) .catch(e => { cb(e, null); }); } /// ================ /// 0.45 /// ================ /** * @private * hashes for 0.45 */ Builder.prototype._hashFourtyFive = function(success, fail, requests) { this.utils.hashLocation1(this.authTicket, this.lat, this.lng, this.accuracy).then((hash1) => { this.utils.hashLocation2(this.lat, this.lng, this.accuracy).then((hash2) => { let signatureData = { location_hash1: hash1, location_hash2: hash2, timestamp: this.time || new Date().getTime(), timestamp_since_start: this.time_since_start || (new Date().getTime() - this.initTime), unknown25: longjs.fromString("16892874496697272497", true, 10) }; for(let field in this.fields) { signatureData[field] = this.fields[field]; } let signature = new PROTO_Signature(signatureData); if(!Array.isArray(requests)) { req = [req]; } let request_hash = []; const authTicket = this.authTicket; for(const request of requests){ this.utils.hashRequest(authTicket, request.encode ? request.encode().toBuffer() : request).then((hashRequest) => { request_hash.push(longjs.fromBits(hashRequest.low, hashRequest.high, true)); if(request_hash.length === requests.length) { signature.request_hash = request_hash; success(signature); }; }, fail); }; }, fail); }, fail); } /// ================ /// 0.51 /// ================ /** * @private * hashes for 0.51 */ Builder.prototype._hashFifthOne = function(requests) { /// This is getting messy because of 0.45 and 0.51 at the same time, can't override these values higher up const byteRequests = []; for(const request of requests) { if(request.encode){ byteRequests.push(request.encode().toBuffer().toString("base64")); } else { byteRequests.push(request.toString("base64")); } } const time_since_start = this.time_since_start || (new Date().getTime() - this.initTime); const timestamp = this.time || new Date().getTime(); /// Do the hashing, get the response back and build the signature return this.utils.hashFiftyOne(this.authTicket, this.lat, this.lng, this.accuracy, timestamp, this.fields.session_hash, byteRequests) .then((response) => { let signatureData = { location_hash1: response.location1, location_hash2: response.location2, timestamp: timestamp, timestamp_since_start: time_since_start, /// Signed, negative number. Unsigned will convert to positive and invalidate unknown25: longjs.fromString("-8832040574896607694", false) }; for(let field in this.fields) { signatureData[field] = this.fields[field]; } let signature = new PROTO_Signature(signatureData); if(!Array.isArray(response.request_hash)) { response.request_hash = [response.request_hash]; } let request_hash = []; const authTicket = this.authTicket; for(let element of response.request_hash) { request_hash.push(longjs.fromString(element, true, 10)); } signature.request_hash = request_hash; return signature; }); } module.exports = Builder;