feat: implement command-line options

This commit is contained in:
Matthew Lopez 2024-06-04 20:31:12 -04:00
parent 90019871e2
commit 8eba27505e
No known key found for this signature in database
GPG Key ID: 302A6EE3D63B7E0E
4 changed files with 86 additions and 60 deletions

View File

@ -46,17 +46,18 @@ This script can also be run in a Docker container.
## Configuration
This script can be configured using environment variables or a `.env` file. Alternatively, it can be run with the `-i` or `--interactive` flag to interactively prompt for all configuration values.
This script can be configured using environment variables, a `.env` file, or individual command-line arguments. Alternatively, it can be run with the `-i` or `--interactive` flag to interactively prompt for all configuration values. Command-line arguments always override environment variables.
| Environment Variable | Description | Default |
| -------------------------- | ------------------------------------------------------------------- | ----------------------------- |
| SSSL_NINTENDO_CA_G3_PATH | Path to Nintendo CA - G3 certificate (may be in DER or PEM format) | `./CACERT_NINTENDO_CA_G3.der` |
| SSSL_NINTENDO_CA_G3_FORMAT | Nintendo CA - G3 certificate format (must be "der" or "pem") | `der` |
| SSSL_CA_PRIVATE_KEY_PATH | Path to private key for forged CA (will generate if not set) | N/A |
| SSSL_SITE_PRIVATE_KEY_PATH | Path to private key for site certificate (will generate if not set) | N/A |
| SSSL_CSR_PATH | Path to CSR (will generate if not set) | N/A |
| SSSL_COMMON_NAME | CN for site certificate (see [the bugs](#the-bugs)) | `*` |
| SSSL_OUTPUT_FOLDER_PATH | Output folder | `./` |
| Environment Variable | Command-line Argument | Description | Default |
| -------------------------- | --------------------------------------- | ------------------------------------------------------------------- | ----------------------------- |
| N/A | `-i`, `--interactive` | Interactively prompt for all configuration values | N/A |
| SSSL_NINTENDO_CA_G3_PATH | `-g`, `--nintendo_g3_path <value>` | Path to Nintendo CA - G3 certificate (may be in DER or PEM format) | `./CACERT_NINTENDO_CA_G3.der` |
| SSSL_NINTENDO_CA_G3_FORMAT | `-f`, `--nintendo_g3_format <value>` | Nintendo CA - G3 certificate format (must be "der" or "pem") | `der` |
| SSSL_CA_PRIVATE_KEY_PATH | `-c`, `--ca_private_key_path <value>` | Path to private key for forged CA (will generate if not set) | N/A |
| SSSL_SITE_PRIVATE_KEY_PATH | `-s`, `--site_private_key_path <value>` | Path to private key for site certificate (will generate if not set) | N/A |
| SSSL_CSR_PATH | `-r`, `--csr_path <value>` | Path to CSR (will generate if not set) | N/A |
| SSSL_COMMON_NAME | `-n`, `--common_name <value>` | CN for site certificate (see [the bugs](#the-bugs)) | `*` |
| SSSL_OUTPUT_FOLDER_PATH | `-o`, `--out <value>` | Output folder | `./` |
## Credits

9
package-lock.json generated
View File

@ -10,6 +10,7 @@
"license": "GPL-3.0",
"dependencies": {
"@colors/colors": "^1.6.0",
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"node-forge": "^1.3.1",
"prompt": "^1.3.0"
@ -304,6 +305,14 @@
"node": ">=0.1.90"
}
},
"node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"engines": {
"node": ">=18"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",

View File

@ -7,6 +7,7 @@
"license": "GPL-3.0",
"dependencies": {
"@colors/colors": "^1.6.0",
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"node-forge": "^1.3.1",
"prompt": "^1.3.0"

115
patch.js
View File

@ -5,35 +5,74 @@ const { asn1, pki, md } = require('node-forge');
const prompt = require('prompt');
const colors = require('@colors/colors/safe');
const dotenv = require('dotenv');
const { program } = require('commander');
const defaultOptions = {
nintendo_ca_g3_path: './CACERT_NINTENDO_CA_G3.der',
nintendo_ca_g3_format: 'der',
ca_private_key_path: undefined,
site_private_key_path: undefined,
csr_path: undefined,
common_name: '*',
output_folder_path: './'
const optionsConfig = {
nintendo_ca_g3_path: {
shortOption: 'g3',
default: './CACERT_NINTENDO_CA_G3.der',
description: 'Path to Nintendo CA - G3 certificate (may be in DER or PEM format, default to this directory)',
env: 'SSSL_NINTENDO_CA_G3_PATH'
},
nintendo_ca_g3_format: {
shortOption: 'f',
default: 'der',
description: 'Nintendo CA - G3 certificate format (must be "der" or "pem")',
env: 'SSSL_NINTENDO_CA_G3_FORMAT'
},
ca_private_key_path: {
shortOption: 'cap',
default: undefined,
description: 'Path to private key for forged CA (will generate if not set)',
env: 'SSSL_CA_PRIVATE_KEY_PATH'
},
site_private_key_path: {
shortOption: 'sp',
default: undefined,
description: 'Path to private key for site certificate (will generate if not set)',
env: 'SSSL_SITE_PRIVATE_KEY_PATH'
},
csr_path: {
shortOption: 'csrp',
default: undefined,
description: 'Path to CSR (will generate if not set)',
env: 'SSSL_CSR_PATH'
},
common_name: {
shortOption: 'cn',
default: '*',
description: 'CN for site certificate (default to "*")',
env: 'SSSL_COMMON_NAME'
},
output_folder_path: {
shortOption: 'o',
default: './',
description: 'Output folder (default to this directory)',
env: 'SSSL_OUTPUT_FOLDER_PATH'
}
};
async function main() {
dotenv.config();
if (process.argv.includes('-i') || process.argv.includes('--interactive')) {
program.option('-i, --interactive', 'Interactively prompt for all configuration values');
for (const [option, config] of Object.entries(optionsConfig)) {
program.option(`-${config.shortOption}, --${option} <value>`, config.description);
}
program.parse(process.argv);
const commandOptions = program.opts();
if (commandOptions.interactive) {
showPrompt();
return;
}
const options = {
nintendo_ca_g3_path: process.env.SSSL_NINTENDO_CA_G3_PATH || defaultOptions.nintendo_ca_g3_path,
nintendo_ca_g3_format: process.env.SSSL_NINTENDO_CA_G3_FORMAT || defaultOptions.nintendo_ca_g3_format,
ca_private_key_path: process.env.SSSL_CA_PRIVATE_KEY_PATH || defaultOptions.ca_private_key_path,
site_private_key_path: process.env.SSSL_SITE_PRIVATE_KEY_PATH || defaultOptions.site_private_key_path,
csr_path: process.env.SSSL_CSR_PATH || defaultOptions.csr_path,
common_name: process.env.SSSL_COMMON_NAME || defaultOptions.common_name,
output_folder_path: process.env.SSSL_OUTPUT_FOLDER_PATH || defaultOptions.output_folder_path
};
const options = {};
for (const [option, config] of Object.entries(optionsConfig)) {
options[option] = commandOptions[option] || process.env[config.env] || config.default;
}
if (validateOptions(options)) {
forgeCertificateChain(options);
@ -47,38 +86,14 @@ async function showPrompt() {
prompt.start();
const options = await prompt.get({
properties: {
nintendo_ca_g3_path: {
description: colors.blue('Path to Nintendo CA - G3 certificate (default to this directory)'),
default: defaultOptions.nintendo_ca_g3_path
},
nintendo_ca_g3_format: {
description: colors.blue('Nintendo CA - G3 certificate format (must be "der" or "pem")'),
default: defaultOptions.nintendo_ca_g3_format
},
ca_private_key_path: {
description: colors.blue('Path to private key for forged CA (will generate if not set)'),
default: defaultOptions.ca_private_key_path
},
site_private_key_path: {
description: colors.blue('Path to private key for site certificate (will generate if not set)'),
default: defaultOptions.site_private_key_path
},
csr_path: {
description: colors.blue('Path to CSR (will generate if not set)'),
default: defaultOptions.csr_path
},
common_name: {
description: colors.blue('CN for site certificate (default to "*")'),
default: defaultOptions.common_name
},
output_folder_path: {
description: colors.blue('Output folder (default to this directory)'),
default: defaultOptions.output_folder_path
}
}
});
const properties = {};
for (const [option, config] of Object.entries(optionsConfig)) {
properties[option] = {
description: colors.blue(config.description),
default: config.default
};
}
const options = await prompt.get({ properties });
if (validateOptions(options)) {
try {