diff --git a/doc/pubkey.but b/doc/pubkey.but index e53dbf00..ed755760 100644 --- a/doc/pubkey.but +++ b/doc/pubkey.but @@ -478,6 +478,82 @@ you have generated an SSH-1 private key using OpenSSH or Hence, the export options are not available if you have generated an SSH-1 key. +\S{puttygen-cli} PuTTYgen command-line configuration + +PuTTYgen supports a set of command-line options to configure many of +the same settings you can select in the GUI. This allows you to start +it up with your own preferences ready-selected, which might be useful +if you generate a lot of keys. (For example, you could make a Windows +shortcut that runs PuTTYgen with some command line options, or a batch +file or Powershell script that you could distribute to a whole +organisation containing your local standards.) + +The options supported on the command line are: + +\dt \cw{\-t} \e{keytype} + +\dd Type of key to generate. You can select \c{rsa}, \c{dsa}, +\c{ecdsa}, \c{eddsa} or \c{rsa1}. See \k{puttygen-keytype}. + +\dt \cw{\-b} \e{bits} + +\dd Size of the key to generate, in bits. See \k{puttygen-strength}. + +\dt \cw{\-\-primes} \e{method} + +\dd Method for generating prime numbers. You can select \c{probable}, +\c{proven}, and \c{proven-even}. See \k{puttygen-primes}. + +\dt \cw{\-\-strong-rsa} + +\dd When generating an RSA key, make sure the prime factors of the key +modulus are \q{strong primes}. A strong prime is a prime number chosen +to have a particular structure that makes certain factoring algorithms +more difficult to apply, so some security standards recommend their +use. However, the most modern factoring algorithms are unaffected, so +this option is probably not worth turning on \e{unless} you have a +local standard that recommends it. + +\dt \cw{\-\-ppk-param} \e{key}\cw{=}\e{value}\cw{,}... + +\dd Allows setting all the same details of the PPK save file format +described in \k{puttygen-save-params}. + +\lcont{ + +Aspects to change are specified as a series of \e{key}\cw{=}\e{value} pairs +separated by commas. The \e{key}s are: + +\dt \cw{version} + +\dd The PPK format version: either \cw{3} or \cw{2}. + +\dt \cw{kdf} + +\dd The variant of Argon2 to use: \cw{argon2id}, \cw{argon2i}, and +\cw{argon2d}. + +\dt \cw{memory} + +\dd The amount of memory needed to decrypt the key, in Kbyte. + +\dt \cw{time} + +\dd Specifies how much time is required to attempt decrypting the key, +in milliseconds. + +\dt \cw{passes} + +\dd Alternative to \cw{time}: specifies the number of hash passes +required to attempt decrypting the key. + +\dt \cw{parallelism} + +\dd Number of parallelisable threads that can be used to decrypt the +key. + +} + \H{pubkey-gettingready} Getting ready for public key authentication Connect to your SSH server using PuTTY with the SSH protocol. When the diff --git a/windows/puttygen.c b/windows/puttygen.c index 920b7339..6cda5817 100644 --- a/windows/puttygen.c +++ b/windows/puttygen.c @@ -629,6 +629,15 @@ static DWORD WINAPI generate_key_thread(void *param) return 0; } +struct InitialParams { + int keybutton; + int primepolicybutton; + bool rsa_strong; + FingerprintType fptype; + int keybits; + int eccurve_index, edcurve_index; +}; + struct MainDlgState { bool generation_thread_exists; bool key_exists; @@ -1393,15 +1402,16 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, cp.ypos = ymax; endbox(&cp); } - ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA); - ui_set_primepolicy(hwnd, state, IDC_PRIMEGEN_PROB); - ui_set_rsa_strong(hwnd, state, false); - ui_set_fptype(hwnd, state, fptype_to_idc(SSH_FPTYPE_DEFAULT)); - SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false); + struct InitialParams *params = (struct InitialParams *)lParam; + ui_set_key_type(hwnd, state, params->keybutton); + ui_set_primepolicy(hwnd, state, params->primepolicybutton); + ui_set_rsa_strong(hwnd, state, params->rsa_strong); + ui_set_fptype(hwnd, state, fptype_to_idc(params->fptype)); + SetDlgItemInt(hwnd, IDC_BITS, params->keybits, false); SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_SETCURSEL, - DEFAULT_ECCURVE_INDEX, 0); + params->eccurve_index, 0); SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_SETCURSEL, - DEFAULT_EDCURVE_INDEX, 0); + params->edcurve_index, 0); /* * Initially, hide the progress bar and the key display, @@ -2013,6 +2023,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) int argc; char **argv; int ret; + struct InitialParams params[1]; dll_hijacking_protection(); @@ -2024,6 +2035,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) */ init_help(); + params->keybutton = IDC_KEYSSH2RSA; + params->primepolicybutton = IDC_PRIMEGEN_PROB; + params->rsa_strong = false; + params->fptype = SSH_FPTYPE_DEFAULT; + params->keybits = DEFAULT_KEY_BITS; + params->eccurve_index = DEFAULT_ECCURVE_INDEX; + params->edcurve_index = DEFAULT_EDCURVE_INDEX; + + save_params = ppk_save_default_parameters; + split_into_argv(cmdline, &argc, &argv, NULL); int argbits = -1; @@ -2052,15 +2073,151 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } else if (match_opt("-restrict-acl", "-restrict_acl", "-restrictacl")) { restrict_process_acl(); + } else if (match_optval("-t")) { + if (!strcmp(val, "rsa") || !strcmp(val, "rsa2")) { + params->keybutton = IDC_KEYSSH2RSA; + } else if (!strcmp(val, "rsa1")) { + params->keybutton = IDC_KEYSSH1; + } else if (!strcmp(val, "dsa") || !strcmp(val, "dss")) { + params->keybutton = IDC_KEYSSH2DSA; + } else if (!strcmp(val, "ecdsa")) { + params->keybutton = IDC_KEYSSH2ECDSA; + } else if (!strcmp(val, "eddsa")) { + params->keybutton = IDC_KEYSSH2EDDSA; + } else if (!strcmp(val, "ed25519")) { + params->keybutton = IDC_KEYSSH2EDDSA; + argbits = 255; + } else if (!strcmp(val, "ed448")) { + params->keybutton = IDC_KEYSSH2EDDSA; + argbits = 448; + } else { + opt_error("unknown key type '%s'\n", val); + } + } else if (match_optval("-b")) { + argbits = atoi(val); + } else if (match_optval("-E")) { + if (!strcmp(val, "md5")) + params->fptype = SSH_FPTYPE_MD5; + else if (!strcmp(val, "sha256")) + params->fptype = SSH_FPTYPE_SHA256; + else + opt_error("unknown fingerprint type '%s'\n", val); + } else if (match_optval("-primes")) { + if (!strcmp(val, "probable") || + !strcmp(val, "probabilistic")) { + params->primepolicybutton = IDC_PRIMEGEN_PROB; + } else if (!strcmp(val, "provable") || + !strcmp(val, "proven") || + !strcmp(val, "simple") || + !strcmp(val, "maurer-simple")) { + params->primepolicybutton = IDC_PRIMEGEN_MAURER_SIMPLE; + } else if (!strcmp(val, "provable-even") || + !strcmp(val, "proven-even") || + !strcmp(val, "even") || + !strcmp(val, "complex") || + !strcmp(val, "maurer-complex")) { + params->primepolicybutton = IDC_PRIMEGEN_MAURER_COMPLEX; + } else { + opt_error("unrecognised prime-generation mode '%s'\n", val); + } + } else if (match_opt("-strong-rsa")) { + params->rsa_strong = true; + } else if (match_optval("-ppk-param", "-ppk-params")) { + char *nextval; + for (; val; val = nextval) { + nextval = strchr(val, ','); + if (nextval) + *nextval++ = '\0'; + + char *optvalue = strchr(val, '='); + if (!optvalue) + opt_error("PPK parameter '%s' expected a value\n", val); + *optvalue++ = '\0'; + + /* Non-numeric options */ + if (!strcmp(val, "kdf")) { + if (!strcmp(optvalue, "Argon2id") || + !strcmp(optvalue, "argon2id")) { + save_params.argon2_flavour = Argon2id; + } else if (!strcmp(optvalue, "Argon2i") || + !strcmp(optvalue, "argon2i")) { + save_params.argon2_flavour = Argon2i; + } else if (!strcmp(optvalue, "Argon2d") || + !strcmp(optvalue, "argon2d")) { + save_params.argon2_flavour = Argon2d; + } else { + opt_error("unrecognised kdf '%s'\n", optvalue); + } + continue; + } + + char *end; + unsigned long n = strtoul(optvalue, &end, 0); + if (!*optvalue || *end) + opt_error("value '%s' for PPK parameter '%s': expected a " + "number\n", optvalue, val); + + if (!strcmp(val, "version")) { + save_params.fmt_version = n; + } else if (!strcmp(val, "memory") || + !strcmp(val, "mem")) { + save_params.argon2_mem = n; + } else if (!strcmp(val, "time")) { + save_params.argon2_passes_auto = true; + save_params.argon2_milliseconds = n; + } else if (!strcmp(val, "passes")) { + save_params.argon2_passes_auto = false; + save_params.argon2_passes = n; + } else if (!strcmp(val, "parallelism") || + !strcmp(val, "parallel")) { + save_params.argon2_parallelism = n; + } else { + opt_error("unrecognised PPK parameter '%s'\n", val); + } + } } else { opt_error("unrecognised option '%s'\n", amo.argv[amo.index]); } } - save_params = ppk_save_default_parameters; + /* Translate argbits into eccurve_index and edcurve_index */ + if (argbits > 0) { + switch (params->keybutton) { + case IDC_KEYSSH2RSA: + case IDC_KEYSSH1: + case IDC_KEYSSH2DSA: + params->keybits = argbits; + break; + case IDC_KEYSSH2ECDSA: { + bool found = false; + for (int j = 0; j < n_ec_nist_curve_lengths; j++) + if (argbits == ec_nist_curve_lengths[j]) { + params->eccurve_index = j; + found = true; + break; + } + if (!found) + opt_error("unsupported ECDSA bit length %d", argbits); + break; + } + case IDC_KEYSSH2EDDSA: { + bool found = false; + for (int j = 0; j < n_ec_ed_curve_lengths; j++) + if (argbits == ec_ed_curve_lengths[j]) { + params->edcurve_index = j; + found = true; + break; + } + if (!found) + opt_error("unsupported EDDSA bit length %d", argbits); + break; + } + } + } random_setup_special(); - ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK; + ret = DialogBoxParam(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc, + (LPARAM)params) != IDOK; cleanup_exit(ret); return ret; /* just in case optimiser complains */