Add command to encrypt/decrypt capture IDs

This commit is contained in:
Samuel Elliott 2022-04-21 16:54:15 +01:00
parent 3f73570096
commit b508fcc072
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
4 changed files with 104 additions and 0 deletions

View File

@ -4,4 +4,5 @@ export * as splatnet2 from './splatnet2.js';
export * as nooklink from './nooklink.js';
export * as pctl from './pctl.js';
export * as androidZncaApiServerFrida from './android-znca-api-server-frida.js';
export * as util from './util.js';
export * as app from './app.js';

20
src/cli/util.ts Normal file
View File

@ -0,0 +1,20 @@
import createDebug from 'debug';
import type { Arguments as ParentArguments } from '../cli.js';
import { Argv, YargsArguments } from '../util.js';
import * as commands from './util/index.js';
const debug = createDebug('cli:util');
export const command = 'util <command>';
export const desc = 'Utilities';
export function builder(yargs: Argv<ParentArguments>) {
for (const command of Object.values(commands)) {
// @ts-expect-error
yargs.command(command);
}
return yargs;
}
export type Arguments = YargsArguments<ReturnType<typeof builder>>;

82
src/cli/util/captureid.ts Normal file
View File

@ -0,0 +1,82 @@
import * as crypto from 'crypto';
import createDebug from 'debug';
import type { Arguments as ParentArguments } from '../../cli.js';
import { Argv } from '../../util.js';
const debug = createDebug('cli:util:captureid');
export const command = 'captureid';
export const desc = 'Encrypt/decrypt capture IDs';
export function builder(yargs: Argv<ParentArguments>) {
return yargs.demandCommand().command('encrypt <titleid>', 'Title ID to Capture ID', yargs => {
return yargs.positional('titleid', {
describe: 'Title ID',
type: 'string',
demandOption: true,
});
}, argv => {
console.log(encrypt(argv.titleid));
}).command('decrypt <captureid>', 'Capture ID to Title ID', yargs => {
return yargs.positional('captureid', {
describe: 'Capture ID',
type: 'string',
demandOption: true,
});
}, argv => {
console.log(decrypt(argv.captureid));
}
const key = Buffer.from('b7ed7a66c80b4b008baf7f0589c08224', 'hex');
/**
* @param {string} tid Hex-encoded 8-byte title ID
* @return {string} Hex-encoded 16-byte capture ID
*/
export function encrypt(tid: string) {
if (typeof tid !== 'string' || !tid.match(/^[0-9A-Fa-f]{16}$/)) {
throw new Error('tid must be a valid title ID');
}
const tidb = Buffer.from('0000000000000000' + tid, 'hex').reverse();
const cipher = crypto.createCipheriv('aes-128-ecb', key, null);
cipher.setAutoPadding(false);
const cidb = Buffer.concat([
cipher.update(tidb),
cipher.final(),
]);
const cid = cidb.toString('hex').toUpperCase();
return cid;
}
/**
* @param {string} cid Hex-encoded 16-byte capture ID
* @return {string} Hex-encoded 8-byte title ID
*/
export function decrypt(cid: string) {
if (typeof cid !== 'string' || !cid.match(/^[0-9A-Fa-f]{32}$/)) {
throw new Error('cid must be a valid capture ID');
}
const cidb = Buffer.from(cid, 'hex');
const cipher = crypto.createDecipheriv('aes-128-ecb', key, null);
cipher.setAutoPadding(false);
const tidb = Buffer.concat([
cipher.update(cidb),
cipher.final(),
]).reverse();
if (!Buffer.alloc(8).equals(tidb.slice(0, 8))) {
throw new Error('Invalid title ID');
}
const tid = tidb.slice(8, 16).toString('hex');
return tid;
}

1
src/cli/util/index.ts Normal file
View File

@ -0,0 +1 @@
export * as captureid from './captureid.js';