Use new account server token format

This commit is contained in:
Jonathan Barrow 2023-04-28 17:02:06 -04:00
parent 9a25a7a9a2
commit 56f248981e
No known key found for this signature in database
GPG Key ID: E86E9FE9049C741F
5 changed files with 38 additions and 79 deletions

View File

@ -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);
}

View File

@ -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,

View File

@ -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;
}

View 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;
}

View File

@ -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 {