mirror of
https://github.com/PretendoNetwork/miiverse-api.git
synced 2026-04-23 08:47:22 -05:00
Use new account server token format
This commit is contained in:
parent
9a25a7a9a2
commit
56f248981e
|
|
@ -21,7 +21,6 @@ export const config: Config = {
|
|||
port: Number(process.env.PN_MIIVERSE_API_CONFIG_HTTP_PORT || '')
|
||||
},
|
||||
account_server_address: process.env.PN_MIIVERSE_API_CONFIG_ACCOUNT_SERVER_ADDRESS || '',
|
||||
account_server_secret: process.env.PN_MIIVERSE_API_CONFIG_ACCOUNT_SERVER_SECRET || '',
|
||||
mongoose: {
|
||||
connection_string: process.env.PN_MIIVERSE_API_CONFIG_MONGO_CONNECTION_STRING || '',
|
||||
options: mongooseConnectOptionsMain
|
||||
|
|
@ -42,7 +41,8 @@ export const config: Config = {
|
|||
port: Number(process.env.PN_MIIVERSE_API_CONFIG_GRPC_ACCOUNT_PORT || ''),
|
||||
api_key: process.env.PN_MIIVERSE_API_CONFIG_GRPC_ACCOUNT_API_KEY || ''
|
||||
}
|
||||
}
|
||||
},
|
||||
aes_key: process.env.PN_MIIVERSE_API_CONFIG_AES_KEY || ''
|
||||
};
|
||||
|
||||
LOG_INFO('Config loaded, checking integrity');
|
||||
|
|
@ -57,11 +57,6 @@ if (!config.account_server_address) {
|
|||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!config.account_server_secret) {
|
||||
LOG_ERROR('Failed to find account server secret. Set the PN_MIIVERSE_API_CONFIG_ACCOUNT_SERVER_SECRET environment variable');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!config.mongoose.connection_string) {
|
||||
LOG_ERROR('Failed to find MongoDB connection string. Set the PN_MIIVERSE_API_CONFIG_MONGO_CONNECTION_STRING environment variable');
|
||||
process.exit(0);
|
||||
|
|
@ -110,4 +105,9 @@ if (!config.grpc.account.port) {
|
|||
if (!config.grpc.account.api_key) {
|
||||
LOG_ERROR('Failed to find account server gRPC API key. Set the PN_MIIVERSE_API_CONFIG_GRPC_ACCOUNT_API_KEY environment variable');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!config.aes_key) {
|
||||
LOG_ERROR('Token AES key is not set. Set the PN_MIIVERSE_API_CONFIG_AES_KEY environment variable to your AES-256-CBC key');
|
||||
process.exit(0);
|
||||
}
|
||||
|
|
@ -240,7 +240,6 @@ router.get('/', async function (request: express.Request, response: express.Resp
|
|||
|
||||
const postBody: FormattedMessage[] = [];
|
||||
for (const message of messages) {
|
||||
console.log(message);
|
||||
postBody.push({
|
||||
post: {
|
||||
body: message.body,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ export interface Config {
|
|||
port: number;
|
||||
};
|
||||
account_server_address: string;
|
||||
account_server_secret: string;
|
||||
mongoose: {
|
||||
connection_string: string;
|
||||
options: mongoose.ConnectOptions;
|
||||
|
|
@ -27,4 +26,5 @@ export interface Config {
|
|||
api_key: string;
|
||||
};
|
||||
};
|
||||
aes_key: string;
|
||||
}
|
||||
8
src/types/common/token.ts
Normal file
8
src/types/common/token.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export interface Token {
|
||||
system_type: number;
|
||||
token_type: number;
|
||||
pid: number;
|
||||
access_level: number;
|
||||
title_id: bigint;
|
||||
expire_time: bigint;
|
||||
}
|
||||
92
src/util.ts
92
src/util.ts
|
|
@ -1,18 +1,15 @@
|
|||
import crypto from 'node:crypto';
|
||||
import { IncomingHttpHeaders } from 'node:http';
|
||||
import NodeRSA from 'node-rsa';
|
||||
import fs from 'fs-extra';
|
||||
import TGA from 'tga';
|
||||
import pako from 'pako';
|
||||
import { PNG } from 'pngjs';
|
||||
import aws from 'aws-sdk';
|
||||
import { createChannel, createClient, Metadata } from 'nice-grpc';
|
||||
import { ParsedQs } from 'qs';
|
||||
import { LOG_ERROR } from '@/logger';
|
||||
import { SafeQs } from '@/types/common/safe-qs';
|
||||
import { ParamPack } from '@/types/common/param-pack';
|
||||
import { config } from '@/config-manager';
|
||||
import { CryptoOptions } from '@/types/common/crypto-options';
|
||||
import { Token } from '@/types/common/token';
|
||||
|
||||
import { FriendsClient, FriendsDefinition } from 'pretendo-grpc-ts/dist/friends/friends_service';
|
||||
import { GetUserFriendPIDsResponse } from 'pretendo-grpc-ts/dist/friends/get_user_friend_pids_rpc';
|
||||
|
|
@ -52,86 +49,41 @@ export function decodeParamPack(paramPack: string): ParamPack {
|
|||
|
||||
export function getPIDFromServiceToken(token: string): number {
|
||||
try {
|
||||
const decoded: Buffer = Buffer.from(token, 'base64');
|
||||
const decryptedToken: Buffer | null = decryptToken(decoded);
|
||||
const decryptedToken: Buffer = decryptToken(Buffer.from(token, 'base64'));
|
||||
|
||||
if (!decryptedToken) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return decryptedToken.readUInt32LE(0x2);
|
||||
const unpackedToken: Token = unpackToken(decryptedToken);
|
||||
|
||||
return unpackedToken.pid;
|
||||
} catch (e) {
|
||||
// TODO - Log this
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function decryptToken(token: Buffer): Buffer | null {
|
||||
const cryptoPath: string = `${__dirname}/../certs/access`;
|
||||
export function decryptToken(token: Buffer): Buffer {
|
||||
const iv: Buffer = Buffer.alloc(16);
|
||||
|
||||
// Access and refresh tokens use a different format since they must be much smaller
|
||||
// Assume a small length means access or refresh token
|
||||
if (token.length <= 32) {
|
||||
const aesKey: Buffer = Buffer.from(fs.readFileSync(`${cryptoPath}/aes.key`, { encoding: 'utf8' }), 'hex');
|
||||
const decipher: crypto.Decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(config.aes_key, 'hex'), iv);
|
||||
|
||||
const iv: Buffer = Buffer.alloc(16);
|
||||
|
||||
const decipher: crypto.Decipher = crypto.createDecipheriv('aes-128-cbc', aesKey, iv);
|
||||
|
||||
return Buffer.concat([
|
||||
decipher.update(token),
|
||||
decipher.final()
|
||||
]);
|
||||
}
|
||||
|
||||
const cryptoOptions: CryptoOptions = {
|
||||
private_key: fs.readFileSync(`${cryptoPath}/private.pem`),
|
||||
hmac_secret: config.account_server_secret
|
||||
};
|
||||
|
||||
const privateKey: NodeRSA = new NodeRSA(cryptoOptions.private_key, 'pkcs1-private-pem', {
|
||||
environment: 'browser',
|
||||
encryptionScheme: {
|
||||
scheme: 'pkcs1_oaep',
|
||||
hash: 'sha256'
|
||||
}
|
||||
});
|
||||
|
||||
const cryptoConfig: Buffer = token.subarray(0, 0x82);
|
||||
const signature: Buffer = token.subarray(0x82, 0x96);
|
||||
const encryptedBody: Buffer = token.subarray(0x96);
|
||||
|
||||
const encryptedAESKey: Buffer = cryptoConfig.subarray(0, 128);
|
||||
const point1: number = cryptoConfig.readInt8(0x80);
|
||||
const point2: number = cryptoConfig.readInt8(0x81);
|
||||
|
||||
const iv: Buffer = Buffer.concat([
|
||||
Buffer.from(encryptedAESKey.subarray(point1, point1 + 8)),
|
||||
Buffer.from(encryptedAESKey.subarray(point2, point2 + 8))
|
||||
return Buffer.concat([
|
||||
decipher.update(token),
|
||||
decipher.final()
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
const decryptedAESKey: Buffer = privateKey.decrypt(encryptedAESKey);
|
||||
|
||||
const decipher: crypto.Decipher = crypto.createDecipheriv('aes-128-cbc', decryptedAESKey, iv);
|
||||
|
||||
const decryptedBody: Buffer = Buffer.concat([
|
||||
decipher.update(encryptedBody),
|
||||
decipher.final()
|
||||
]);
|
||||
|
||||
const hmac: crypto.Hmac = crypto.createHmac('sha1', cryptoOptions.hmac_secret).update(decryptedBody);
|
||||
const calculatedSignature: Buffer = hmac.digest();
|
||||
|
||||
if (Buffer.compare(calculatedSignature, signature) !== 0) {
|
||||
LOG_ERROR('Token signature did not match');
|
||||
return null;
|
||||
}
|
||||
|
||||
return decryptedBody;
|
||||
} catch (error) {
|
||||
LOG_ERROR('Failed to decrypt token. Probably a NNID from the topics request');
|
||||
return null;
|
||||
}
|
||||
export function unpackToken(token: Buffer): Token {
|
||||
return {
|
||||
system_type: token.readUInt8(0x0),
|
||||
token_type: token.readUInt8(0x1),
|
||||
pid: token.readUInt32LE(0x2),
|
||||
expire_time: token.readBigUInt64LE(0x6),
|
||||
title_id: token.readBigUInt64LE(0xE),
|
||||
access_level: token.readUInt8(0x16)
|
||||
};
|
||||
}
|
||||
|
||||
export function processPainting(painting: string): Buffer | null {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user