Skip to content

Commit

Permalink
Add actions to support linking PS and Smogon accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
mia-pi-git committed Sep 28, 2024
1 parent 75b2885 commit d31ddcc
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
6 changes: 6 additions & 0 deletions config/config-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ exports.watchconfig = true;
*/
exports.restartip = null;

/**
* An IP to allow Smogon acc-linking requests from.
* @type {null | string}
*/
exports.smogonip = null;

/**
* Custom actions for your loginserver.
* @type {{[k: string]: import('../src/server').QueryHandler} | null}
Expand Down
28 changes: 27 additions & 1 deletion src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import {Ladder} from './ladder';
import {Replays} from './replays';
import {ActionError, QueryHandler, Server} from './server';
import {Session} from './user';
import {toID, updateserver, bash, time, escapeHTML} from './utils';
import {
toID, updateserver, bash, time, escapeHTML, encrypt, decrypt, makeEncryptKey,
} from './utils';
import * as tables from './tables';
import {SQL} from './database';
import IPTools from './ip-tools';

const OAUTH_TOKEN_TIME = 2 * 7 * 24 * 60 * 60 * 1000;
const SMOGON_KEY = makeEncryptKey();

async function getOAuthClient(clientId?: string, origin?: string) {
if (!clientId) throw new ActionError("No client_id provided.");
Expand Down Expand Up @@ -902,6 +905,29 @@ export const actions: {[k: string]: QueryHandler} = {
}
return {password: pw};
},

// sent by ps server
'smogon/encrypt'(params) {
if (this.getIp() !== Config.restartip) {
throw new ActionError("Access denied.");
}
params.username = toID(params.username);
if (!params.username) {
throw new ActionError("Invalid PS username provided.");
}
return {encrypted_username: encrypt(SMOGON_KEY, params.username)};
},

// sent by smogon to validate given encrypted name
'smogon/validate'(params) {
if (this.getIp() !== Config.smogonip) {
throw new ActionError("Access denied.");
}
if (!params.encrypted_name || !toID(params.encrypted_name)) {
throw new ActionError("No encrypted name provided.");
}
return {decrypted_name: decrypt(SMOGON_KEY, params.encrypted_name)};
},
};

if (Config.actions) {
Expand Down
43 changes: 43 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,46 @@ export function escapeHTML(str: string | number) {
.replace(/"/g, '"')
.replace(/'/g, ''');
}

const IV_LENGTH = 16;

const NONCE_LENGTH = 20;

export function encrypt(key: Buffer, text: string) {
const nonce = crypto.randomBytes(NONCE_LENGTH);
const iv = Buffer.alloc(IV_LENGTH);
nonce.copy(iv);

const cipher = crypto.createCipheriv('aes-256-ctr', key, iv);
const encrypted = cipher.update(text.toString());
return Buffer.concat([nonce, encrypted, cipher.final()]).toString('base64');
}

export function decrypt(key: Buffer, text: string) {
const message = Buffer.from(text, 'base64');
const iv = Buffer.alloc(IV_LENGTH);
message.copy(iv, 0, 0, NONCE_LENGTH);
const decipher = crypto.createDecipheriv('aes-256-ctr', key, iv);
let decrypted = decipher.update(message.slice(NONCE_LENGTH));
try {
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
} catch (err) {
return null;
}
}

// 32 chars - 256 bytes
export function makeEncryptKey(len = 32) {
let chars = 'abcdefghijklmnopqrstuvwxyz';
chars += chars.toUpperCase();
chars += "1234567890";
chars += "()-={}|!@#$%^&*?><:";

let key = "";
for (let i = 0; i < len; i++) {
key += chars[Math.round(Math.random() * chars.length)];
}

return crypto.pbkdf2Sync(key, Math.random() + "", 10000, len, 'sha512');
}

0 comments on commit d31ddcc

Please sign in to comment.