2000-10-19 15:43:08 +00:00
|
|
|
/*
|
2004-04-27 12:31:57 +00:00
|
|
|
* PuTTY key generation front end (Windows).
|
2000-10-19 15:43:08 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
#include <stdio.h>
|
2000-10-24 10:47:49 +00:00
|
|
|
#include <stdlib.h>
|
2011-10-02 14:14:21 +00:00
|
|
|
#include <assert.h>
|
2000-10-19 15:43:08 +00:00
|
|
|
|
|
|
|
#include "putty.h"
|
|
|
|
#include "ssh.h"
|
2020-02-23 15:29:40 +00:00
|
|
|
#include "sshkeygen.h"
|
2015-12-22 12:43:31 +00:00
|
|
|
#include "licence.h"
|
2021-04-23 05:19:05 +00:00
|
|
|
#include "security-api.h"
|
2021-02-22 20:15:11 +00:00
|
|
|
#include "puttygen-rc.h"
|
2003-10-12 13:46:12 +00:00
|
|
|
|
|
|
|
#include <commctrl.h>
|
2000-10-19 15:43:08 +00:00
|
|
|
|
2003-02-07 13:54:34 +00:00
|
|
|
#ifdef MSVC4
|
|
|
|
#define ICON_BIG 1
|
|
|
|
#endif
|
|
|
|
|
2005-08-10 18:31:24 +00:00
|
|
|
#define WM_DONEKEY (WM_APP + 1)
|
2000-10-19 15:43:08 +00:00
|
|
|
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
#define DEFAULT_KEY_BITS 2048
|
2020-03-02 07:05:19 +00:00
|
|
|
#define DEFAULT_ECCURVE_INDEX 0
|
|
|
|
#define DEFAULT_EDCURVE_INDEX 0
|
2000-10-19 15:43:08 +00:00
|
|
|
|
2024-09-24 16:50:19 +00:00
|
|
|
static Filename *cmdline_keyfile = NULL;
|
2022-04-02 15:18:08 +00:00
|
|
|
static ptrlen cmdline_demo_keystr;
|
2024-09-24 16:50:19 +00:00
|
|
|
static Filename *demo_screenshot_filename = NULL;
|
2002-08-06 17:48:14 +00:00
|
|
|
|
2002-10-09 18:09:42 +00:00
|
|
|
/*
|
|
|
|
* Print a modal (Really Bad) message box and perform a fatal exit.
|
|
|
|
*/
|
2015-05-15 10:15:42 +00:00
|
|
|
void modalfatalbox(const char *fmt, ...)
|
2002-10-09 18:09:42 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
2002-11-07 19:49:03 +00:00
|
|
|
char *stuff;
|
2002-10-09 18:09:42 +00:00
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2002-11-07 19:49:03 +00:00
|
|
|
stuff = dupvprintf(fmt, ap);
|
2002-10-09 18:09:42 +00:00
|
|
|
va_end(ap);
|
|
|
|
MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
|
2019-09-08 19:29:00 +00:00
|
|
|
MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
|
2002-11-07 19:49:03 +00:00
|
|
|
sfree(stuff);
|
2002-10-09 18:09:42 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2013-07-22 07:11:44 +00:00
|
|
|
/*
|
|
|
|
* Print a non-fatal message box and do not exit.
|
|
|
|
*/
|
2015-05-15 10:15:42 +00:00
|
|
|
void nonfatal(const char *fmt, ...)
|
2013-07-22 07:11:44 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char *stuff;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
stuff = dupvprintf(fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
MessageBox(NULL, stuff, "PuTTYgen Error",
|
2019-09-08 19:29:00 +00:00
|
|
|
MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
|
2013-07-22 07:11:44 +00:00
|
|
|
sfree(stuff);
|
|
|
|
}
|
|
|
|
|
2000-10-19 15:43:08 +00:00
|
|
|
/* ----------------------------------------------------------------------
|
2020-02-23 15:29:40 +00:00
|
|
|
* ProgressReceiver implementation.
|
2000-10-19 15:43:08 +00:00
|
|
|
*/
|
2020-02-23 15:29:40 +00:00
|
|
|
|
2001-09-22 20:52:21 +00:00
|
|
|
#define PROGRESSRANGE 65535
|
2020-02-23 15:29:40 +00:00
|
|
|
|
|
|
|
struct progressphase {
|
|
|
|
double startpoint, total;
|
2020-02-29 17:11:01 +00:00
|
|
|
/* For exponential phases */
|
2020-02-23 15:29:40 +00:00
|
|
|
double exp_probability, exp_current_value;
|
|
|
|
};
|
|
|
|
|
2000-10-19 15:43:08 +00:00
|
|
|
struct progress {
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
size_t nphases, phasessize;
|
|
|
|
struct progressphase *phases, *currphase;
|
2020-02-23 15:29:40 +00:00
|
|
|
|
|
|
|
double scale;
|
2000-10-19 15:43:08 +00:00
|
|
|
HWND progbar;
|
2020-02-23 15:29:40 +00:00
|
|
|
|
|
|
|
ProgressReceiver rec;
|
2000-10-19 15:43:08 +00:00
|
|
|
};
|
|
|
|
|
2020-02-29 17:11:01 +00:00
|
|
|
static ProgressPhase win_progress_add_linear(
|
|
|
|
ProgressReceiver *prog, double overall_cost) {
|
|
|
|
struct progress *p = container_of(prog, struct progress, rec);
|
|
|
|
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
sgrowarray(p->phases, p->phasessize, p->nphases);
|
2020-02-29 17:11:01 +00:00
|
|
|
int phase = p->nphases++;
|
|
|
|
|
|
|
|
p->phases[phase].total = overall_cost;
|
|
|
|
|
|
|
|
ProgressPhase ph = { .n = phase };
|
|
|
|
return ph;
|
|
|
|
}
|
|
|
|
|
2020-02-23 15:29:40 +00:00
|
|
|
static ProgressPhase win_progress_add_probabilistic(
|
|
|
|
ProgressReceiver *prog, double cost_per_attempt, double probability) {
|
|
|
|
struct progress *p = container_of(prog, struct progress, rec);
|
|
|
|
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
sgrowarray(p->phases, p->phasessize, p->nphases);
|
2020-02-23 15:29:40 +00:00
|
|
|
int phase = p->nphases++;
|
|
|
|
|
|
|
|
p->phases[phase].exp_probability = 1.0 - probability;
|
|
|
|
p->phases[phase].exp_current_value = 1.0;
|
|
|
|
/* Expected number of attempts = 1 / probability of attempt succeeding */
|
|
|
|
p->phases[phase].total = cost_per_attempt / probability;
|
|
|
|
|
|
|
|
ProgressPhase ph = { .n = phase };
|
|
|
|
return ph;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void win_progress_ready(ProgressReceiver *prog)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2020-02-23 15:29:40 +00:00
|
|
|
struct progress *p = container_of(prog, struct progress, rec);
|
|
|
|
|
|
|
|
double total = 0;
|
|
|
|
for (int i = 0; i < p->nphases; i++) {
|
|
|
|
p->phases[i].startpoint = total;
|
|
|
|
total += p->phases[i].total;
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
2020-02-23 15:29:40 +00:00
|
|
|
p->scale = PROGRESSRANGE / total;
|
|
|
|
|
|
|
|
SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void win_progress_start_phase(ProgressReceiver *prog,
|
|
|
|
ProgressPhase phase)
|
|
|
|
{
|
|
|
|
struct progress *p = container_of(prog, struct progress, rec);
|
|
|
|
|
|
|
|
assert(phase.n < p->nphases);
|
|
|
|
p->currphase = &p->phases[phase.n];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void win_progress_update(struct progress *p, double phasepos)
|
|
|
|
{
|
|
|
|
double position = (p->currphase->startpoint +
|
|
|
|
p->currphase->total * phasepos);
|
|
|
|
position *= p->scale;
|
|
|
|
if (position < 0)
|
|
|
|
position = 0;
|
|
|
|
if (position > PROGRESSRANGE)
|
|
|
|
position = PROGRESSRANGE;
|
|
|
|
|
|
|
|
SendMessage(p->progbar, PBM_SETPOS, (WPARAM)position, 0);
|
|
|
|
}
|
|
|
|
|
2020-02-29 17:11:01 +00:00
|
|
|
static void win_progress_report(ProgressReceiver *prog, double progress)
|
|
|
|
{
|
|
|
|
struct progress *p = container_of(prog, struct progress, rec);
|
|
|
|
|
|
|
|
win_progress_update(p, progress);
|
|
|
|
}
|
|
|
|
|
2020-02-23 15:29:40 +00:00
|
|
|
static void win_progress_report_attempt(ProgressReceiver *prog)
|
|
|
|
{
|
|
|
|
struct progress *p = container_of(prog, struct progress, rec);
|
|
|
|
|
|
|
|
p->currphase->exp_current_value *= p->currphase->exp_probability;
|
|
|
|
win_progress_update(p, 1.0 - p->currphase->exp_current_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void win_progress_report_phase_complete(ProgressReceiver *prog)
|
|
|
|
{
|
|
|
|
struct progress *p = container_of(prog, struct progress, rec);
|
|
|
|
|
|
|
|
win_progress_update(p, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const ProgressReceiverVtable win_progress_vt = {
|
Change vtable defs to use C99 designated initialisers.
This is a sweeping change applied across the whole code base by a spot
of Emacs Lisp. Now, everywhere I declare a vtable filled with function
pointers (and the occasional const data member), all the members of
the vtable structure are initialised by name using the '.fieldname =
value' syntax introduced in C99.
We were already using this syntax for a handful of things in the new
key-generation progress report system, so it's not new to the code
base as a whole.
The advantage is that now, when a vtable only declares a subset of the
available fields, I can initialise the rest to NULL or zero just by
leaving them out. This is most dramatic in a couple of the outlying
vtables in things like psocks (which has a ConnectionLayerVtable
containing only one non-NULL method), but less dramatically, it means
that the new 'flags' field in BackendVtable can be completely left out
of every backend definition except for the SUPDUP one which defines it
to a nonzero value. Similarly, the test_for_upstream method only used
by SSH doesn't have to be mentioned in the rest of the backends;
network Plugs for listening sockets don't have to explicitly null out
'receive' and 'sent', and vice versa for 'accepting', and so on.
While I'm at it, I've normalised the declarations so they don't use
the unnecessarily verbose 'struct' keyword. Also a handful of them
weren't const; now they are.
2020-03-10 21:06:29 +00:00
|
|
|
.add_linear = win_progress_add_linear,
|
|
|
|
.add_probabilistic = win_progress_add_probabilistic,
|
|
|
|
.ready = win_progress_ready,
|
|
|
|
.start_phase = win_progress_start_phase,
|
|
|
|
.report = win_progress_report,
|
|
|
|
.report_attempt = win_progress_report_attempt,
|
|
|
|
.report_phase_complete = win_progress_report_phase_complete,
|
2020-02-23 15:29:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void win_progress_initialise(struct progress *p)
|
|
|
|
{
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
p->nphases = p->phasessize = 0;
|
|
|
|
p->phases = p->currphase = NULL;
|
2020-02-23 15:29:40 +00:00
|
|
|
p->rec.vt = &win_progress_vt;
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
static void win_progress_cleanup(struct progress *p)
|
|
|
|
{
|
|
|
|
sfree(p->phases);
|
|
|
|
}
|
|
|
|
|
2000-10-19 15:43:08 +00:00
|
|
|
struct PassphraseProcStruct {
|
2011-10-02 14:14:21 +00:00
|
|
|
char **passphrase;
|
2000-10-19 15:43:08 +00:00
|
|
|
char *comment;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dialog-box function for the passphrase box.
|
|
|
|
*/
|
2015-08-11 12:22:09 +00:00
|
|
|
static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
|
2022-08-03 19:48:46 +00:00
|
|
|
WPARAM wParam, LPARAM lParam)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2011-10-02 14:14:21 +00:00
|
|
|
static char **passphrase = NULL;
|
2000-10-19 15:43:08 +00:00
|
|
|
struct PassphraseProcStruct *p;
|
|
|
|
|
|
|
|
switch (msg) {
|
|
|
|
case WM_INITDIALOG:
|
2019-09-08 19:29:00 +00:00
|
|
|
SetForegroundWindow(hwnd);
|
|
|
|
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
|
|
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Centre the window.
|
|
|
|
*/
|
|
|
|
{ /* centre the window */
|
|
|
|
RECT rs, rd;
|
|
|
|
HWND hw;
|
|
|
|
|
|
|
|
hw = GetDesktopWindow();
|
|
|
|
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
|
|
|
|
MoveWindow(hwnd,
|
|
|
|
(rs.right + rs.left + rd.left - rd.right) / 2,
|
|
|
|
(rs.bottom + rs.top + rd.top - rd.bottom) / 2,
|
|
|
|
rd.right - rd.left, rd.bottom - rd.top, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
p = (struct PassphraseProcStruct *) lParam;
|
|
|
|
passphrase = p->passphrase;
|
|
|
|
if (p->comment)
|
|
|
|
SetDlgItemText(hwnd, 101, p->comment);
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(*passphrase);
|
|
|
|
*passphrase = dupstr("");
|
2019-09-08 19:29:00 +00:00
|
|
|
SetDlgItemText(hwnd, 102, *passphrase);
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_COMMAND:
|
2019-09-08 19:29:00 +00:00
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
|
|
if (*passphrase)
|
|
|
|
EndDialog(hwnd, 1);
|
|
|
|
else
|
|
|
|
MessageBeep(0);
|
|
|
|
return 0;
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hwnd, 0);
|
|
|
|
return 0;
|
|
|
|
case 102: /* edit box */
|
|
|
|
if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(*passphrase);
|
|
|
|
*passphrase = GetDlgItemText_alloc(hwnd, 102);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_CLOSE:
|
2019-09-08 19:29:00 +00:00
|
|
|
EndDialog(hwnd, 0);
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-22 20:15:11 +00:00
|
|
|
static void try_get_dlg_item_uint32(HWND hwnd, int id, uint32_t *out)
|
|
|
|
{
|
|
|
|
char buf[128];
|
|
|
|
if (!GetDlgItemText(hwnd, id, buf, sizeof(buf)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!*buf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
char *end;
|
|
|
|
unsigned long val = strtoul(buf, &end, 10);
|
|
|
|
if (*end)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((val >> 16) >> 16)
|
|
|
|
return;
|
|
|
|
|
|
|
|
*out = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ppk_save_parameters save_params;
|
|
|
|
|
|
|
|
struct PPKParams {
|
|
|
|
ppk_save_parameters params;
|
|
|
|
uint32_t time_passes, time_ms;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dialog-box function for the passphrase box.
|
|
|
|
*/
|
|
|
|
static INT_PTR CALLBACK PPKParamsProc(HWND hwnd, UINT msg,
|
|
|
|
WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
struct PPKParams *pp;
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
if (msg == WM_INITDIALOG) {
|
|
|
|
pp = (struct PPKParams *)lParam;
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pp);
|
|
|
|
} else {
|
|
|
|
pp = (struct PPKParams *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (msg) {
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
SetForegroundWindow(hwnd);
|
|
|
|
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
|
|
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
|
|
|
2021-04-20 15:06:01 +00:00
|
|
|
if (has_help())
|
|
|
|
SetWindowLongPtr(hwnd, GWL_EXSTYLE,
|
|
|
|
GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
|
|
|
|
WS_EX_CONTEXTHELP);
|
|
|
|
|
2021-02-22 20:15:11 +00:00
|
|
|
/*
|
|
|
|
* Centre the window.
|
|
|
|
*/
|
|
|
|
{ /* centre the window */
|
|
|
|
RECT rs, rd;
|
|
|
|
HWND hw;
|
|
|
|
|
|
|
|
hw = GetDesktopWindow();
|
|
|
|
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
|
|
|
|
MoveWindow(hwnd,
|
|
|
|
(rs.right + rs.left + rd.left - rd.right) / 2,
|
|
|
|
(rs.bottom + rs.top + rd.top - rd.bottom) / 2,
|
|
|
|
rd.right - rd.left, rd.bottom - rd.top, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
CheckRadioButton(hwnd, IDC_PPKVER_2, IDC_PPKVER_3,
|
|
|
|
IDC_PPKVER_2 + (pp->params.fmt_version - 2));
|
|
|
|
|
2021-02-23 18:26:50 +00:00
|
|
|
CheckRadioButton(
|
|
|
|
hwnd, IDC_KDF_ARGON2ID, IDC_KDF_ARGON2D,
|
|
|
|
(pp->params.argon2_flavour == Argon2id ? IDC_KDF_ARGON2ID :
|
|
|
|
pp->params.argon2_flavour == Argon2i ? IDC_KDF_ARGON2I :
|
|
|
|
/* pp->params.argon2_flavour == Argon2d ? */ IDC_KDF_ARGON2D));
|
|
|
|
|
2021-02-22 20:15:11 +00:00
|
|
|
buf = dupprintf("%"PRIu32, pp->params.argon2_mem);
|
|
|
|
SetDlgItemText(hwnd, IDC_ARGON2_MEM, buf);
|
|
|
|
sfree(buf);
|
|
|
|
|
|
|
|
if (pp->params.argon2_passes_auto) {
|
|
|
|
CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO,
|
|
|
|
IDC_PPK_AUTO_YES);
|
|
|
|
buf = dupprintf("%"PRIu32, pp->time_ms);
|
|
|
|
SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf);
|
|
|
|
sfree(buf);
|
|
|
|
} else {
|
|
|
|
CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO,
|
|
|
|
IDC_PPK_AUTO_NO);
|
|
|
|
buf = dupprintf("%"PRIu32, pp->time_passes);
|
|
|
|
SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf);
|
|
|
|
sfree(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = dupprintf("%"PRIu32, pp->params.argon2_parallelism);
|
|
|
|
SetDlgItemText(hwnd, IDC_ARGON2_PARALLEL, buf);
|
|
|
|
sfree(buf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
|
|
EndDialog(hwnd, 1);
|
|
|
|
return 0;
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hwnd, 0);
|
|
|
|
return 0;
|
|
|
|
case IDC_PPKVER_2:
|
|
|
|
pp->params.fmt_version = 2;
|
|
|
|
return 0;
|
|
|
|
case IDC_PPKVER_3:
|
|
|
|
pp->params.fmt_version = 3;
|
|
|
|
return 0;
|
2021-02-23 18:26:50 +00:00
|
|
|
case IDC_KDF_ARGON2ID:
|
|
|
|
pp->params.argon2_flavour = Argon2id;
|
|
|
|
return 0;
|
|
|
|
case IDC_KDF_ARGON2I:
|
|
|
|
pp->params.argon2_flavour = Argon2i;
|
|
|
|
return 0;
|
|
|
|
case IDC_KDF_ARGON2D:
|
|
|
|
pp->params.argon2_flavour = Argon2d;
|
|
|
|
return 0;
|
2021-02-22 20:15:11 +00:00
|
|
|
case IDC_ARGON2_MEM:
|
|
|
|
try_get_dlg_item_uint32(hwnd, IDC_ARGON2_MEM,
|
|
|
|
&pp->params.argon2_mem);
|
|
|
|
return 0;
|
|
|
|
case IDC_PPK_AUTO_YES:
|
|
|
|
pp->params.argon2_passes_auto = true;
|
|
|
|
buf = dupprintf("%"PRIu32, pp->time_ms);
|
|
|
|
SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf);
|
|
|
|
sfree(buf);
|
|
|
|
return 0;
|
|
|
|
case IDC_PPK_AUTO_NO:
|
|
|
|
pp->params.argon2_passes_auto = false;
|
|
|
|
buf = dupprintf("%"PRIu32, pp->time_passes);
|
|
|
|
SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf);
|
|
|
|
sfree(buf);
|
|
|
|
return 0;
|
|
|
|
case IDC_ARGON2_TIME:
|
|
|
|
try_get_dlg_item_uint32(hwnd, IDC_ARGON2_TIME,
|
|
|
|
pp->params.argon2_passes_auto ?
|
|
|
|
&pp->time_ms : &pp->time_passes);
|
|
|
|
return 0;
|
|
|
|
case IDC_ARGON2_PARALLEL:
|
|
|
|
try_get_dlg_item_uint32(hwnd, IDC_ARGON2_PARALLEL,
|
|
|
|
&pp->params.argon2_parallelism);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
2021-04-20 15:06:01 +00:00
|
|
|
case WM_HELP: {
|
|
|
|
int id = ((LPHELPINFO)lParam)->iCtrlId;
|
|
|
|
const char *topic = NULL;
|
|
|
|
switch (id) {
|
|
|
|
case IDC_PPKVER_STATIC:
|
|
|
|
case IDC_PPKVER_2:
|
|
|
|
case IDC_PPKVER_3:
|
|
|
|
topic = WINHELP_CTX_puttygen_ppkver; break;
|
|
|
|
case IDC_KDF_STATIC:
|
|
|
|
case IDC_KDF_ARGON2ID:
|
|
|
|
case IDC_KDF_ARGON2I:
|
|
|
|
case IDC_KDF_ARGON2D:
|
|
|
|
case IDC_ARGON2_MEM_STATIC:
|
|
|
|
case IDC_ARGON2_MEM:
|
|
|
|
case IDC_ARGON2_MEM_STATIC2:
|
|
|
|
case IDC_ARGON2_TIME_STATIC:
|
|
|
|
case IDC_ARGON2_TIME:
|
|
|
|
case IDC_PPK_AUTO_YES:
|
|
|
|
case IDC_PPK_AUTO_NO:
|
|
|
|
case IDC_ARGON2_PARALLEL_STATIC:
|
|
|
|
case IDC_ARGON2_PARALLEL:
|
|
|
|
topic = WINHELP_CTX_puttygen_kdfparam; break;
|
|
|
|
}
|
|
|
|
if (topic) {
|
2022-08-03 19:48:46 +00:00
|
|
|
launch_help(hwnd, topic);
|
2021-04-20 15:06:01 +00:00
|
|
|
} else {
|
2022-08-03 19:48:46 +00:00
|
|
|
MessageBeep(0);
|
2021-04-20 15:06:01 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2021-02-22 20:15:11 +00:00
|
|
|
case WM_CLOSE:
|
|
|
|
EndDialog(hwnd, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2000-10-19 15:43:08 +00:00
|
|
|
/*
|
|
|
|
* Prompt for a key file. Assumes the filename buffer is of size
|
|
|
|
* FILENAME_MAX.
|
|
|
|
*/
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
static bool prompt_keyfile(HWND hwnd, char *dlgtitle,
|
|
|
|
char *filename, bool save, bool ppk)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-10-19 15:43:08 +00:00
|
|
|
OPENFILENAME of;
|
|
|
|
memset(&of, 0, sizeof(of));
|
|
|
|
of.hwndOwner = hwnd;
|
2002-08-06 17:35:34 +00:00
|
|
|
if (ppk) {
|
2019-09-08 19:29:00 +00:00
|
|
|
of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
|
|
|
|
"All Files (*.*)\0*\0\0\0";
|
|
|
|
of.lpstrDefExt = ".ppk";
|
2002-08-06 17:35:34 +00:00
|
|
|
} else {
|
2019-09-08 19:29:00 +00:00
|
|
|
of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
|
2002-08-06 17:35:34 +00:00
|
|
|
}
|
2000-10-19 15:43:08 +00:00
|
|
|
of.lpstrCustomFilter = NULL;
|
|
|
|
of.nFilterIndex = 1;
|
2001-05-06 14:35:20 +00:00
|
|
|
of.lpstrFile = filename;
|
|
|
|
*filename = '\0';
|
2000-10-19 15:43:08 +00:00
|
|
|
of.nMaxFile = FILENAME_MAX;
|
|
|
|
of.lpstrFileTitle = NULL;
|
|
|
|
of.lpstrTitle = dlgtitle;
|
|
|
|
of.Flags = 0;
|
2018-10-29 19:50:29 +00:00
|
|
|
return request_file(NULL, &of, false, save);
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dialog-box function for the Licence box.
|
|
|
|
*/
|
2015-08-11 12:22:09 +00:00
|
|
|
static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
|
2022-08-03 19:48:46 +00:00
|
|
|
WPARAM wParam, LPARAM lParam)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-10-19 15:43:08 +00:00
|
|
|
switch (msg) {
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
case WM_INITDIALOG: {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* Centre the window.
|
|
|
|
*/
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
RECT rs, rd;
|
|
|
|
HWND hw;
|
2019-09-08 19:29:00 +00:00
|
|
|
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
hw = GetDesktopWindow();
|
|
|
|
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
|
|
|
|
MoveWindow(hwnd,
|
|
|
|
(rs.right + rs.left + rd.left - rd.right) / 2,
|
|
|
|
(rs.bottom + rs.top + rd.top - rd.bottom) / 2,
|
|
|
|
rd.right - rd.left, rd.bottom - rd.top, true);
|
2001-03-03 11:54:34 +00:00
|
|
|
|
2015-12-22 12:43:31 +00:00
|
|
|
SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
|
2019-09-08 19:29:00 +00:00
|
|
|
return 1;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_COMMAND:
|
2019-09-08 19:29:00 +00:00
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hwnd, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_CLOSE:
|
2019-09-08 19:29:00 +00:00
|
|
|
EndDialog(hwnd, 1);
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dialog-box function for the About box.
|
|
|
|
*/
|
2015-08-11 12:22:09 +00:00
|
|
|
static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
|
2022-08-03 19:48:46 +00:00
|
|
|
WPARAM wParam, LPARAM lParam)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-10-19 15:43:08 +00:00
|
|
|
switch (msg) {
|
|
|
|
case WM_INITDIALOG:
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* Centre the window.
|
|
|
|
*/
|
|
|
|
{ /* centre the window */
|
|
|
|
RECT rs, rd;
|
|
|
|
HWND hw;
|
|
|
|
|
|
|
|
hw = GetDesktopWindow();
|
|
|
|
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
|
|
|
|
MoveWindow(hwnd,
|
|
|
|
(rs.right + rs.left + rd.left - rd.right) / 2,
|
|
|
|
(rs.bottom + rs.top + rd.top - rd.bottom) / 2,
|
|
|
|
rd.right - rd.left, rd.bottom - rd.top, true);
|
|
|
|
}
|
2001-03-03 11:54:34 +00:00
|
|
|
|
2015-12-22 10:18:48 +00:00
|
|
|
{
|
2017-01-21 14:55:53 +00:00
|
|
|
char *buildinfo_text = buildinfo("\r\n");
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
char *text = dupprintf(
|
|
|
|
"PuTTYgen\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s",
|
|
|
|
ver, buildinfo_text,
|
|
|
|
"\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
|
2017-01-21 14:55:53 +00:00
|
|
|
sfree(buildinfo_text);
|
2015-12-22 10:18:48 +00:00
|
|
|
SetDlgItemText(hwnd, 1000, text);
|
2021-04-06 20:14:51 +00:00
|
|
|
MakeDlgItemBorderless(hwnd, 1000);
|
2015-12-22 10:18:48 +00:00
|
|
|
sfree(text);
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
return 1;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_COMMAND:
|
2019-09-08 19:29:00 +00:00
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hwnd, 1);
|
|
|
|
return 0;
|
|
|
|
case 101:
|
|
|
|
EnableWindow(hwnd, 0);
|
|
|
|
DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
|
|
|
|
EnableWindow(hwnd, 1);
|
|
|
|
SetActiveWindow(hwnd);
|
|
|
|
return 0;
|
|
|
|
case 102:
|
|
|
|
/* Load web browser */
|
|
|
|
ShellExecute(hwnd, "open",
|
|
|
|
"https://www.chiark.greenend.org.uk/~sgtatham/putty/",
|
|
|
|
0, 0, SW_SHOWDEFAULT);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_CLOSE:
|
2019-09-08 19:29:00 +00:00
|
|
|
EndDialog(hwnd, 1);
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-02 07:05:19 +00:00
|
|
|
typedef enum {RSA, DSA, ECDSA, EDDSA} keytype;
|
2014-11-01 09:14:19 +00:00
|
|
|
|
2000-10-19 15:43:08 +00:00
|
|
|
/*
|
|
|
|
* Thread to generate a key.
|
|
|
|
*/
|
|
|
|
struct rsa_key_thread_params {
|
2019-09-08 19:29:00 +00:00
|
|
|
HWND progressbar; /* notify this with progress */
|
|
|
|
HWND dialog; /* notify this on completion */
|
|
|
|
int key_bits; /* bits in key modulus (RSA, DSA) */
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
int curve_bits; /* bits in elliptic curve (ECDSA) */
|
2014-11-01 09:14:19 +00:00
|
|
|
keytype keytype;
|
2020-02-29 06:43:28 +00:00
|
|
|
const PrimeGenerationPolicy *primepolicy;
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
bool rsa_strong;
|
2014-11-01 09:14:19 +00:00
|
|
|
union {
|
2019-01-04 06:51:44 +00:00
|
|
|
RSAKey *key;
|
2021-04-22 17:28:35 +00:00
|
|
|
struct dsa_key *dsakey;
|
Complete rewrite of PuTTY's bignum library.
The old 'Bignum' data type is gone completely, and so is sshbn.c. In
its place is a new thing called 'mp_int', handled by an entirely new
library module mpint.c, with API differences both large and small.
The main aim of this change is that the new library should be free of
timing- and cache-related side channels. I've written the code so that
it _should_ - assuming I haven't made any mistakes - do all of its
work without either control flow or memory addressing depending on the
data words of the input numbers. (Though, being an _arbitrary_
precision library, it does have to at least depend on the sizes of the
numbers - but there's a 'formal' size that can vary separately from
the actual magnitude of the represented integer, so if you want to
keep it secret that your number is actually small, it should work fine
to have a very long mp_int and just happen to store 23 in it.) So I've
done all my conditionalisation by means of computing both answers and
doing bit-masking to swap the right one into place, and all loops over
the words of an mp_int go up to the formal size rather than the actual
size.
I haven't actually tested the constant-time property in any rigorous
way yet (I'm still considering the best way to do it). But this code
is surely at the very least a big improvement on the old version, even
if I later find a few more things to fix.
I've also completely rewritten the low-level elliptic curve arithmetic
from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c
than it is to the SSH end of the code. The new elliptic curve code
keeps all coordinates in Montgomery-multiplication transformed form to
speed up all the multiplications mod the same prime, and only converts
them back when you ask for the affine coordinates. Also, I adopted
extended coordinates for the Edwards curve implementation.
sshecc.c has also had a near-total rewrite in the course of switching
it over to the new system. While I was there, I've separated ECDSA and
EdDSA more completely - they now have separate vtables, instead of a
single vtable in which nearly every function had a big if statement in
it - and also made the externally exposed types for an ECDSA key and
an ECDH context different.
A minor new feature: since the new arithmetic code includes a modular
square root function, we can now support the compressed point
representation for the NIST curves. We seem to have been getting along
fine without that so far, but it seemed a shame not to put it in,
since it was suddenly easy.
In sshrsa.c, one major change is that I've removed the RSA blinding
step in rsa_privkey_op, in which we randomise the ciphertext before
doing the decryption. The purpose of that was to avoid timing leaks
giving away the plaintext - but the new arithmetic code should take
that in its stride in the course of also being careful enough to avoid
leaking the _private key_, which RSA blinding had no way to do
anything about in any case.
Apart from those specific points, most of the rest of the changes are
more or less mechanical, just changing type names and translating code
into the new API.
2018-12-31 13:53:41 +00:00
|
|
|
struct ecdsa_key *eckey;
|
|
|
|
struct eddsa_key *edkey;
|
2014-11-01 09:14:19 +00:00
|
|
|
};
|
2000-10-19 15:43:08 +00:00
|
|
|
};
|
2016-03-30 10:28:59 +00:00
|
|
|
static DWORD WINAPI generate_key_thread(void *param)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-10-19 15:43:08 +00:00
|
|
|
struct rsa_key_thread_params *params =
|
2019-09-08 19:29:00 +00:00
|
|
|
(struct rsa_key_thread_params *) param;
|
2000-10-19 15:43:08 +00:00
|
|
|
struct progress prog;
|
|
|
|
prog.progbar = params->progressbar;
|
|
|
|
|
2020-02-23 15:29:40 +00:00
|
|
|
win_progress_initialise(&prog);
|
2001-11-12 09:19:57 +00:00
|
|
|
|
2020-02-29 06:43:28 +00:00
|
|
|
PrimeGenerationContext *pgc = primegen_new_context(params->primepolicy);
|
2020-02-29 09:10:47 +00:00
|
|
|
|
2014-11-01 09:14:19 +00:00
|
|
|
if (params->keytype == DSA)
|
2021-04-22 17:28:35 +00:00
|
|
|
dsa_generate(params->dsakey, params->key_bits, pgc, &prog.rec);
|
2014-11-01 09:45:20 +00:00
|
|
|
else if (params->keytype == ECDSA)
|
2020-02-23 15:29:40 +00:00
|
|
|
ecdsa_generate(params->eckey, params->curve_bits);
|
2020-03-02 07:05:19 +00:00
|
|
|
else if (params->keytype == EDDSA)
|
|
|
|
eddsa_generate(params->edkey, params->curve_bits);
|
2001-09-22 20:52:21 +00:00
|
|
|
else
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
rsa_generate(params->key, params->key_bits, params->rsa_strong,
|
|
|
|
pgc, &prog.rec);
|
2020-02-29 09:10:47 +00:00
|
|
|
|
|
|
|
primegen_free_context(pgc);
|
2000-10-19 15:43:08 +00:00
|
|
|
|
|
|
|
PostMessage(params->dialog, WM_DONEKEY, 0, 0);
|
|
|
|
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
win_progress_cleanup(&prog);
|
|
|
|
|
2000-12-12 10:33:13 +00:00
|
|
|
sfree(params);
|
2000-10-19 15:43:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-01-15 18:30:34 +00:00
|
|
|
struct InitialParams {
|
|
|
|
int keybutton;
|
|
|
|
int primepolicybutton;
|
|
|
|
bool rsa_strong;
|
|
|
|
FingerprintType fptype;
|
|
|
|
int keybits;
|
|
|
|
int eccurve_index, edcurve_index;
|
|
|
|
};
|
|
|
|
|
2000-10-19 15:43:08 +00:00
|
|
|
struct MainDlgState {
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool generation_thread_exists;
|
|
|
|
bool key_exists;
|
2022-01-08 11:20:27 +00:00
|
|
|
int entropy_got, entropy_required;
|
|
|
|
strbuf *entropy;
|
Windows PuTTYgen: rate-limit entropy counter increment.
Some pointing devices (e.g. gaming mice) can be set to send
mouse-movement events at an extremely high sample rate like 1kHz. This
apparently translates into Windows genuinely sending WM_MOUSEMOVE
messages at that rate. So if you're using one of those mice, then
PuTTYgen's mouse-based entropy collection system will fill its buffer
almost immediately, and give you no perceptible time to actually wave
the mouse around.
I think that in that situation, there's also likely to be a strong
correlation between the contents of successive movement events,
because human-controlled mouse movements aren't fractals - the more
you zoom in on a little piece of one, the more it starts to look more
and more like something moving in a straight line at constant speed,
because the deviations from that happen on a larger time scale than
you're seeing.
So this commit adds a rate limit, not on the _collection_ of the data
(we'll still take all the bits we can get, thanks!), but on the rate
at which we increment the _counter_ for how much entropy we think
we've collected so far. That way, the user still has to spend time
wiggling the mouse back and forth in a way that varies with muscle
motions and introduces randomness.
2022-01-08 12:09:36 +00:00
|
|
|
ULONG entropy_prev_msgtime;
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
int key_bits, curve_bits;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool ssh2;
|
2014-11-01 09:14:19 +00:00
|
|
|
keytype keytype;
|
2020-02-29 06:43:28 +00:00
|
|
|
const PrimeGenerationPolicy *primepolicy;
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
bool rsa_strong;
|
2021-03-13 09:58:14 +00:00
|
|
|
FingerprintType fptype;
|
2019-09-08 19:29:00 +00:00
|
|
|
char **commentptr; /* points to key.comment or ssh2key.comment */
|
2019-01-04 06:51:44 +00:00
|
|
|
ssh2_userkey ssh2key;
|
2014-11-01 09:14:19 +00:00
|
|
|
union {
|
2019-01-04 06:51:44 +00:00
|
|
|
RSAKey key;
|
2021-04-22 17:28:35 +00:00
|
|
|
struct dsa_key dsakey;
|
Complete rewrite of PuTTY's bignum library.
The old 'Bignum' data type is gone completely, and so is sshbn.c. In
its place is a new thing called 'mp_int', handled by an entirely new
library module mpint.c, with API differences both large and small.
The main aim of this change is that the new library should be free of
timing- and cache-related side channels. I've written the code so that
it _should_ - assuming I haven't made any mistakes - do all of its
work without either control flow or memory addressing depending on the
data words of the input numbers. (Though, being an _arbitrary_
precision library, it does have to at least depend on the sizes of the
numbers - but there's a 'formal' size that can vary separately from
the actual magnitude of the represented integer, so if you want to
keep it secret that your number is actually small, it should work fine
to have a very long mp_int and just happen to store 23 in it.) So I've
done all my conditionalisation by means of computing both answers and
doing bit-masking to swap the right one into place, and all loops over
the words of an mp_int go up to the formal size rather than the actual
size.
I haven't actually tested the constant-time property in any rigorous
way yet (I'm still considering the best way to do it). But this code
is surely at the very least a big improvement on the old version, even
if I later find a few more things to fix.
I've also completely rewritten the low-level elliptic curve arithmetic
from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c
than it is to the SSH end of the code. The new elliptic curve code
keeps all coordinates in Montgomery-multiplication transformed form to
speed up all the multiplications mod the same prime, and only converts
them back when you ask for the affine coordinates. Also, I adopted
extended coordinates for the Edwards curve implementation.
sshecc.c has also had a near-total rewrite in the course of switching
it over to the new system. While I was there, I've separated ECDSA and
EdDSA more completely - they now have separate vtables, instead of a
single vtable in which nearly every function had a big if statement in
it - and also made the externally exposed types for an ECDSA key and
an ECDH context different.
A minor new feature: since the new arithmetic code includes a modular
square root function, we can now support the compressed point
representation for the NIST curves. We seem to have been getting along
fine without that so far, but it seemed a shame not to put it in,
since it was suddenly easy.
In sshrsa.c, one major change is that I've removed the RSA blinding
step in rsa_privkey_op, in which we randomise the ciphertext before
doing the decryption. The purpose of that was to avoid timing leaks
giving away the plaintext - but the new arithmetic code should take
that in its stride in the course of also being careful enough to avoid
leaking the _private key_, which RSA blinding had no way to do
anything about in any case.
Apart from those specific points, most of the rest of the changes are
more or less mechanical, just changing type names and translating code
into the new API.
2018-12-31 13:53:41 +00:00
|
|
|
struct ecdsa_key eckey;
|
|
|
|
struct eddsa_key edkey;
|
2014-11-01 09:14:19 +00:00
|
|
|
};
|
2002-05-18 09:20:41 +00:00
|
|
|
HMENU filemenu, keymenu, cvtmenu;
|
2000-10-19 15:43:08 +00:00
|
|
|
};
|
|
|
|
|
Windows PuTTYgen: rate-limit entropy counter increment.
Some pointing devices (e.g. gaming mice) can be set to send
mouse-movement events at an extremely high sample rate like 1kHz. This
apparently translates into Windows genuinely sending WM_MOUSEMOVE
messages at that rate. So if you're using one of those mice, then
PuTTYgen's mouse-based entropy collection system will fill its buffer
almost immediately, and give you no perceptible time to actually wave
the mouse around.
I think that in that situation, there's also likely to be a strong
correlation between the contents of successive movement events,
because human-controlled mouse movements aren't fractals - the more
you zoom in on a little piece of one, the more it starts to look more
and more like something moving in a straight line at constant speed,
because the deviations from that happen on a larger time scale than
you're seeing.
So this commit adds a rate limit, not on the _collection_ of the data
(we'll still take all the bits we can get, thanks!), but on the rate
at which we increment the _counter_ for how much entropy we think
we've collected so far. That way, the user still has to spend time
wiggling the mouse back and forth in a way that varies with muscle
motions and introduces randomness.
2022-01-08 12:09:36 +00:00
|
|
|
/*
|
|
|
|
* Rate limit for incrementing the entropy_got counter.
|
|
|
|
*
|
|
|
|
* Some pointing devices (e.g. gaming mice) can be set to send
|
|
|
|
* mouse-movement events at an extremely high sample rate like 1kHz.
|
|
|
|
* In that situation, there's likely to be a strong correlation
|
|
|
|
* between the contents of successive movement events, so you have to
|
|
|
|
* regard the mouse movements as containing less entropy each.
|
|
|
|
*
|
|
|
|
* A reasonably simple approach to this is to continue to buffer all
|
|
|
|
* mouse data, but limit the rate at which we increment the counter
|
|
|
|
* for how much entropy we think we've collected. That way, the user
|
|
|
|
* still has to spend time wiggling the mouse back and forth in a way
|
|
|
|
* that varies with muscle motions and introduces randomness.
|
|
|
|
*/
|
|
|
|
#define ENTROPY_RATE_LIMIT 10 /* in units of GetMessageTime(), i.e. ms */
|
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
static void hidemany(HWND hwnd, const int *ids, bool hideit)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2000-10-19 15:43:08 +00:00
|
|
|
while (*ids) {
|
2019-09-08 19:29:00 +00:00
|
|
|
ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-30 13:23:43 +00:00
|
|
|
enum {
|
|
|
|
controlidstart = 100,
|
|
|
|
IDC_QUIT,
|
|
|
|
IDC_TITLE,
|
|
|
|
IDC_BOX_KEY,
|
|
|
|
IDC_NOKEY,
|
|
|
|
IDC_GENERATING,
|
|
|
|
IDC_PROGRESS,
|
|
|
|
IDC_PKSTATIC, IDC_KEYDISPLAY,
|
2022-07-30 13:41:08 +00:00
|
|
|
IDC_CERTSTATIC, IDC_CERTMOREINFO,
|
2022-07-30 13:23:43 +00:00
|
|
|
IDC_FPSTATIC, IDC_FINGERPRINT,
|
|
|
|
IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
|
|
|
|
IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
|
|
|
|
IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
|
|
|
|
IDC_BOX_ACTIONS,
|
|
|
|
IDC_GENSTATIC, IDC_GENERATE,
|
|
|
|
IDC_LOADSTATIC, IDC_LOAD,
|
|
|
|
IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
|
|
|
|
IDC_BOX_PARAMS,
|
|
|
|
IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
|
|
|
|
IDC_KEYSSH2ECDSA, IDC_KEYSSH2EDDSA,
|
|
|
|
IDC_PRIMEGEN_PROB, IDC_PRIMEGEN_MAURER_SIMPLE, IDC_PRIMEGEN_MAURER_COMPLEX,
|
|
|
|
IDC_RSA_STRONG,
|
|
|
|
IDC_FPTYPE_SHA256, IDC_FPTYPE_MD5,
|
|
|
|
IDC_PPK_PARAMS,
|
|
|
|
IDC_BITSSTATIC, IDC_BITS,
|
|
|
|
IDC_ECCURVESTATIC, IDC_ECCURVE,
|
|
|
|
IDC_EDCURVESTATIC, IDC_EDCURVE,
|
|
|
|
IDC_NOTHINGSTATIC,
|
|
|
|
IDC_ABOUT,
|
|
|
|
IDC_GIVEHELP,
|
|
|
|
IDC_IMPORT,
|
|
|
|
IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
|
|
|
|
IDC_EXPORT_SSHCOM,
|
|
|
|
IDC_ADDCERT, IDC_REMCERT,
|
|
|
|
};
|
|
|
|
|
2022-07-30 13:41:08 +00:00
|
|
|
static void setupbigedit1(HWND hwnd, RSAKey *key)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2022-07-30 13:41:08 +00:00
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_CERTSTATIC), SW_HIDE);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_CERTMOREINFO), SW_HIDE);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_PKSTATIC), SW_SHOW);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_KEYDISPLAY), SW_SHOW);
|
|
|
|
|
|
|
|
SetDlgItemText(hwnd, IDC_PKSTATIC,
|
2019-09-08 19:29:00 +00:00
|
|
|
"&Public key for pasting into authorized_keys file:");
|
2022-07-30 13:41:08 +00:00
|
|
|
|
|
|
|
char *buffer = ssh1_pubkey_str(key);
|
|
|
|
SetDlgItemText(hwnd, IDC_KEYDISPLAY, buffer);
|
2000-12-12 10:33:13 +00:00
|
|
|
sfree(buffer);
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
|
2022-07-30 13:41:08 +00:00
|
|
|
static void setupbigedit2(HWND hwnd, ssh2_userkey *key)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2022-07-30 13:41:08 +00:00
|
|
|
if (ssh_key_alg(key->key)->is_certificate) {
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_CERTSTATIC), SW_SHOW);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_CERTMOREINFO), SW_SHOW);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_PKSTATIC), SW_HIDE);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_KEYDISPLAY), SW_HIDE);
|
|
|
|
|
|
|
|
SetDlgItemText(hwnd, IDC_CERTSTATIC,
|
|
|
|
"This public key contains an OpenSSH certificate.");
|
|
|
|
} else {
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_CERTSTATIC), SW_HIDE);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_CERTMOREINFO), SW_HIDE);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_PKSTATIC), SW_SHOW);
|
|
|
|
ShowWindow(GetDlgItem(hwnd, IDC_KEYDISPLAY), SW_SHOW);
|
|
|
|
|
|
|
|
SetDlgItemText(hwnd, IDC_PKSTATIC, "&Public key for pasting into "
|
|
|
|
"OpenSSH authorized_keys file:");
|
|
|
|
|
|
|
|
char *buffer = ssh2_pubkey_openssh_str(key);
|
|
|
|
SetDlgItemText(hwnd, IDC_KEYDISPLAY, buffer);
|
|
|
|
sfree(buffer);
|
|
|
|
}
|
2001-03-03 11:54:34 +00:00
|
|
|
}
|
|
|
|
|
2001-11-25 14:31:46 +00:00
|
|
|
/*
|
|
|
|
* Warn about the obsolescent key file format.
|
|
|
|
*/
|
|
|
|
void old_keyfile_warning(void)
|
|
|
|
{
|
|
|
|
static const char mbtitle[] = "PuTTY Key File Warning";
|
|
|
|
static const char message[] =
|
2019-09-08 19:29:00 +00:00
|
|
|
"You are loading an SSH-2 private key which has an\n"
|
|
|
|
"old version of the file format. This means your key\n"
|
|
|
|
"file is not fully tamperproof. Future versions of\n"
|
|
|
|
"PuTTY may stop supporting this private key format,\n"
|
|
|
|
"so we recommend you convert your key to the new\n"
|
|
|
|
"format.\n"
|
|
|
|
"\n"
|
|
|
|
"Once the key is loaded into PuTTYgen, you can perform\n"
|
|
|
|
"this conversion simply by saving it again.";
|
2001-11-25 14:31:46 +00:00
|
|
|
|
|
|
|
MessageBox(NULL, message, mbtitle, MB_OK);
|
|
|
|
}
|
|
|
|
|
2002-05-15 20:07:11 +00:00
|
|
|
static const int nokey_ids[] = { IDC_NOKEY, 0 };
|
|
|
|
static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
|
2022-07-30 13:41:08 +00:00
|
|
|
static const int gotkey_ids_unconditional[] = {
|
2002-05-15 20:07:11 +00:00
|
|
|
IDC_FPSTATIC, IDC_FINGERPRINT,
|
|
|
|
IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
|
|
|
|
IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
|
|
|
|
IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
|
|
|
|
};
|
2022-07-30 13:41:08 +00:00
|
|
|
static const int gotkey_ids_conditional[] = {
|
|
|
|
IDC_PKSTATIC, IDC_KEYDISPLAY,
|
|
|
|
IDC_CERTSTATIC, IDC_CERTMOREINFO,
|
|
|
|
};
|
2002-05-15 20:07:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Small UI helper function to switch the state of the main dialog
|
|
|
|
* by enabling and disabling controls and menu items.
|
|
|
|
*/
|
|
|
|
void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
|
|
|
|
{
|
|
|
|
int type;
|
|
|
|
|
|
|
|
switch (status) {
|
2019-09-08 19:29:00 +00:00
|
|
|
case 0: /* no key */
|
|
|
|
hidemany(hwnd, nokey_ids, false);
|
|
|
|
hidemany(hwnd, generating_ids, true);
|
2022-07-30 13:41:08 +00:00
|
|
|
hidemany(hwnd, gotkey_ids_unconditional, true);
|
|
|
|
hidemany(hwnd, gotkey_ids_conditional, true);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
|
2014-11-01 09:45:20 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
|
2020-03-02 07:05:19 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
|
2014-11-01 09:45:20 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
|
|
|
|
MF_ENABLED|MF_BYCOMMAND);
|
2020-03-02 07:05:19 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA,
|
2015-05-09 14:02:54 +00:00
|
|
|
MF_ENABLED|MF_BYCOMMAND);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
|
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
|
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
|
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
2022-04-19 16:59:46 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_ADDCERT, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_REMCERT, MF_GRAYED|MF_BYCOMMAND);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
|
|
|
case 1: /* generating key */
|
|
|
|
hidemany(hwnd, nokey_ids, true);
|
|
|
|
hidemany(hwnd, generating_ids, false);
|
2022-07-30 13:41:08 +00:00
|
|
|
hidemany(hwnd, gotkey_ids_unconditional, true);
|
|
|
|
hidemany(hwnd, gotkey_ids_conditional, true);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
|
2014-11-01 09:45:20 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
|
2020-03-02 07:05:19 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 0);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
|
2014-11-01 09:45:20 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
|
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
2020-03-02 07:05:19 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA,
|
2015-05-09 14:02:54 +00:00
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
|
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
|
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
|
|
|
|
MF_GRAYED|MF_BYCOMMAND);
|
2022-04-19 16:59:46 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_ADDCERT, MF_GRAYED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_REMCERT, MF_GRAYED|MF_BYCOMMAND);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
2002-05-15 20:07:11 +00:00
|
|
|
case 2:
|
2019-09-08 19:29:00 +00:00
|
|
|
hidemany(hwnd, nokey_ids, true);
|
|
|
|
hidemany(hwnd, generating_ids, true);
|
2022-07-30 13:41:08 +00:00
|
|
|
hidemany(hwnd, gotkey_ids_unconditional, false);
|
|
|
|
// gotkey_ids_conditional will be unhidden by setupbigedit2
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
|
2014-11-01 09:45:20 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
|
2020-03-02 07:05:19 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
|
2014-11-01 09:45:20 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
|
|
|
|
MF_ENABLED|MF_BYCOMMAND);
|
2020-03-02 07:05:19 +00:00
|
|
|
EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA,
|
2015-05-09 14:02:54 +00:00
|
|
|
MF_ENABLED|MF_BYCOMMAND);
|
2019-09-08 19:29:00 +00:00
|
|
|
EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
|
|
|
|
/*
|
|
|
|
* Enable export menu items if and only if the key type
|
|
|
|
* supports this kind of export.
|
|
|
|
*/
|
|
|
|
type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
|
2002-05-15 20:07:11 +00:00
|
|
|
#define do_export_menuitem(x,y) \
|
2002-05-18 09:20:41 +00:00
|
|
|
EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
|
2019-09-08 19:29:00 +00:00
|
|
|
(import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
|
|
|
|
do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
|
|
|
|
do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
|
|
|
|
do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
|
2002-05-15 20:07:11 +00:00
|
|
|
#undef do_export_menuitem
|
2022-04-19 16:59:46 +00:00
|
|
|
/*
|
|
|
|
* Enable certificate menu items similarly.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
bool add_cert_allowed = false, rem_cert_allowed = false;
|
|
|
|
|
|
|
|
if (state->ssh2 && state->ssh2key.key) {
|
|
|
|
const ssh_keyalg *alg = ssh_key_alg(state->ssh2key.key);
|
|
|
|
if (alg->is_certificate) {
|
|
|
|
/* If there's a certificate, we can remove it */
|
|
|
|
rem_cert_allowed = true;
|
|
|
|
/* And reset to the base algorithm for the next check */
|
|
|
|
alg = alg->base_alg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now, do we have any certified version of this alg? */
|
|
|
|
for (size_t i = 0; i < n_keyalgs; i++) {
|
|
|
|
if (all_keyalgs[i]->base_alg == alg) {
|
|
|
|
add_cert_allowed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EnableMenuItem(state->keymenu, IDC_ADDCERT, MF_BYCOMMAND |
|
|
|
|
(add_cert_allowed ? MF_ENABLED : MF_GRAYED));
|
|
|
|
EnableMenuItem(state->keymenu, IDC_REMCERT, MF_BYCOMMAND |
|
|
|
|
(rem_cert_allowed ? MF_ENABLED : MF_GRAYED));
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
2002-05-15 20:07:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
/*
|
|
|
|
* Helper functions to set the key type, taking care of keeping the
|
|
|
|
* menu and radio button selections in sync and also showing/hiding
|
|
|
|
* the appropriate size/curve control for the current key type.
|
|
|
|
*/
|
|
|
|
void ui_update_key_type_ctrls(HWND hwnd)
|
|
|
|
{
|
2020-03-02 07:05:19 +00:00
|
|
|
enum { BITS, ECCURVE, EDCURVE, NOTHING } which;
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
static const int bits_ids[] = {
|
|
|
|
IDC_BITSSTATIC, IDC_BITS, 0
|
|
|
|
};
|
2020-03-02 07:05:19 +00:00
|
|
|
static const int eccurve_ids[] = {
|
|
|
|
IDC_ECCURVESTATIC, IDC_ECCURVE, 0
|
|
|
|
};
|
|
|
|
static const int edcurve_ids[] = {
|
|
|
|
IDC_EDCURVESTATIC, IDC_EDCURVE, 0
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
};
|
|
|
|
static const int nothing_ids[] = {
|
|
|
|
IDC_NOTHINGSTATIC, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1) ||
|
|
|
|
IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA) ||
|
|
|
|
IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
|
|
|
|
which = BITS;
|
|
|
|
} else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
|
2020-03-02 07:05:19 +00:00
|
|
|
which = ECCURVE;
|
|
|
|
} else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) {
|
|
|
|
which = EDCURVE;
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
} else {
|
2020-03-02 07:05:19 +00:00
|
|
|
/* Currently not used since Ed25519 stopped being the only
|
|
|
|
* thing in its class, but I'll keep it here in case it comes
|
|
|
|
* in useful again */
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
which = NOTHING;
|
|
|
|
}
|
|
|
|
|
|
|
|
hidemany(hwnd, bits_ids, which != BITS);
|
2020-03-02 07:05:19 +00:00
|
|
|
hidemany(hwnd, eccurve_ids, which != ECCURVE);
|
|
|
|
hidemany(hwnd, edcurve_ids, which != EDCURVE);
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
hidemany(hwnd, nothing_ids, which != NOTHING);
|
|
|
|
}
|
|
|
|
void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button)
|
|
|
|
{
|
2020-03-02 07:05:19 +00:00
|
|
|
CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2EDDSA, button);
|
|
|
|
CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2EDDSA,
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
button, MF_BYCOMMAND);
|
|
|
|
ui_update_key_type_ctrls(hwnd);
|
|
|
|
}
|
2020-02-29 06:43:28 +00:00
|
|
|
void ui_set_primepolicy(HWND hwnd, struct MainDlgState *state, int option)
|
|
|
|
{
|
|
|
|
CheckMenuRadioItem(state->keymenu, IDC_PRIMEGEN_PROB,
|
|
|
|
IDC_PRIMEGEN_MAURER_COMPLEX, option, MF_BYCOMMAND);
|
|
|
|
switch (option) {
|
|
|
|
case IDC_PRIMEGEN_PROB:
|
|
|
|
state->primepolicy = &primegen_probabilistic;
|
|
|
|
break;
|
|
|
|
case IDC_PRIMEGEN_MAURER_SIMPLE:
|
|
|
|
state->primepolicy = &primegen_provable_maurer_simple;
|
|
|
|
break;
|
|
|
|
case IDC_PRIMEGEN_MAURER_COMPLEX:
|
|
|
|
state->primepolicy = &primegen_provable_maurer_complex;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
void ui_set_rsa_strong(HWND hwnd, struct MainDlgState *state, bool enable)
|
|
|
|
{
|
|
|
|
state->rsa_strong = enable;
|
|
|
|
CheckMenuItem(state->keymenu, IDC_RSA_STRONG,
|
|
|
|
(enable ? MF_CHECKED : 0) | MF_BYCOMMAND);
|
|
|
|
}
|
2021-03-13 09:58:14 +00:00
|
|
|
static FingerprintType idc_to_fptype(int option)
|
|
|
|
{
|
|
|
|
switch (option) {
|
|
|
|
case IDC_FPTYPE_SHA256:
|
|
|
|
return SSH_FPTYPE_SHA256;
|
|
|
|
case IDC_FPTYPE_MD5:
|
|
|
|
return SSH_FPTYPE_MD5;
|
|
|
|
default:
|
|
|
|
unreachable("bad control id in idc_to_fptype");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static int fptype_to_idc(FingerprintType fptype)
|
|
|
|
{
|
|
|
|
switch (fptype) {
|
|
|
|
case SSH_FPTYPE_SHA256:
|
|
|
|
return IDC_FPTYPE_SHA256;
|
|
|
|
case SSH_FPTYPE_MD5:
|
|
|
|
return IDC_FPTYPE_MD5;
|
|
|
|
default:
|
|
|
|
unreachable("bad fptype in fptype_to_idc");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void ui_set_fptype(HWND hwnd, struct MainDlgState *state, int option)
|
|
|
|
{
|
|
|
|
CheckMenuRadioItem(state->keymenu, IDC_FPTYPE_SHA256,
|
|
|
|
IDC_FPTYPE_MD5, option, MF_BYCOMMAND);
|
|
|
|
|
|
|
|
state->fptype = idc_to_fptype(option);
|
|
|
|
|
|
|
|
if (state->key_exists && state->ssh2) {
|
|
|
|
char *fp = ssh2_fingerprint(state->ssh2key.key, state->fptype);
|
|
|
|
SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
|
|
|
|
sfree(fp);
|
|
|
|
}
|
|
|
|
}
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
|
2022-04-19 16:59:46 +00:00
|
|
|
static void update_ui_after_ssh2_pubkey_change(
|
|
|
|
HWND hwnd, struct MainDlgState *state)
|
|
|
|
{
|
|
|
|
/* Smaller version of update_ui_after_load which doesn't need to
|
|
|
|
* be told things like the passphrase, which we aren't changing
|
|
|
|
* anyway */
|
|
|
|
char *savecomment = state->ssh2key.comment;
|
|
|
|
state->ssh2key.comment = NULL;
|
|
|
|
char *fp = ssh2_fingerprint(state->ssh2key.key, state->fptype);
|
|
|
|
state->ssh2key.comment = savecomment;
|
|
|
|
|
|
|
|
SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
|
|
|
|
sfree(fp);
|
|
|
|
|
2022-07-30 13:41:08 +00:00
|
|
|
setupbigedit2(hwnd, &state->ssh2key);
|
2022-04-19 16:59:46 +00:00
|
|
|
}
|
|
|
|
|
2022-04-02 15:15:53 +00:00
|
|
|
static void update_ui_after_load(HWND hwnd, struct MainDlgState *state,
|
|
|
|
const char *passphrase, int type,
|
|
|
|
RSAKey *newkey1, ssh2_userkey *newkey2)
|
|
|
|
{
|
|
|
|
SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, passphrase);
|
|
|
|
SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, passphrase);
|
|
|
|
|
|
|
|
if (type == SSH_KEYTYPE_SSH1) {
|
|
|
|
char *fingerprint, *savecomment;
|
|
|
|
|
|
|
|
state->ssh2 = false;
|
|
|
|
state->commentptr = &state->key.comment;
|
|
|
|
state->key = *newkey1; /* structure copy */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the key fingerprint.
|
|
|
|
*/
|
|
|
|
savecomment = state->key.comment;
|
|
|
|
state->key.comment = NULL;
|
|
|
|
fingerprint = rsa_ssh1_fingerprint(&state->key);
|
|
|
|
state->key.comment = savecomment;
|
|
|
|
SetDlgItemText(hwnd, IDC_FINGERPRINT, fingerprint);
|
|
|
|
sfree(fingerprint);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a decimal representation of the key, for pasting
|
|
|
|
* into .ssh/authorized_keys on a Unix box.
|
|
|
|
*/
|
2022-07-30 13:41:08 +00:00
|
|
|
setupbigedit1(hwnd, &state->key);
|
2022-04-02 15:15:53 +00:00
|
|
|
} else {
|
|
|
|
state->ssh2 = true;
|
|
|
|
state->commentptr = &state->ssh2key.comment;
|
|
|
|
state->ssh2key = *newkey2; /* structure copy */
|
|
|
|
sfree(newkey2);
|
|
|
|
|
2022-04-19 16:59:46 +00:00
|
|
|
update_ui_after_ssh2_pubkey_change(hwnd, state);
|
2022-04-02 15:15:53 +00:00
|
|
|
}
|
|
|
|
SetDlgItemText(hwnd, IDC_COMMENTEDIT,
|
|
|
|
*state->commentptr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, hide the progress bar and show the key data.
|
|
|
|
*/
|
|
|
|
ui_set_state(hwnd, state, 2);
|
|
|
|
state->key_exists = true;
|
|
|
|
}
|
|
|
|
|
2002-08-06 17:48:14 +00:00
|
|
|
void load_key_file(HWND hwnd, struct MainDlgState *state,
|
2019-09-08 19:29:00 +00:00
|
|
|
Filename *filename, bool was_import_cmd)
|
2002-08-06 17:48:14 +00:00
|
|
|
{
|
2011-10-02 14:14:21 +00:00
|
|
|
char *passphrase;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool needs_pass;
|
2002-08-06 17:48:14 +00:00
|
|
|
int type, realtype;
|
|
|
|
int ret;
|
2005-02-27 23:01:11 +00:00
|
|
|
const char *errmsg = NULL;
|
2002-08-06 17:48:14 +00:00
|
|
|
char *comment;
|
2019-01-04 06:51:44 +00:00
|
|
|
RSAKey newkey1;
|
|
|
|
ssh2_userkey *newkey2 = NULL;
|
2002-08-06 17:48:14 +00:00
|
|
|
|
2011-10-02 11:01:57 +00:00
|
|
|
type = realtype = key_type(filename);
|
2002-08-06 17:48:14 +00:00
|
|
|
if (type != SSH_KEYTYPE_SSH1 &&
|
2019-09-08 19:29:00 +00:00
|
|
|
type != SSH_KEYTYPE_SSH2 &&
|
|
|
|
!import_possible(type)) {
|
|
|
|
char *msg = dupprintf("Couldn't load private key (%s)",
|
|
|
|
key_type_to_str(type));
|
2020-02-02 10:00:42 +00:00
|
|
|
message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
|
2023-05-29 12:28:11 +00:00
|
|
|
false, HELPCTXID(errors_cantloadkey));
|
2019-09-08 19:29:00 +00:00
|
|
|
sfree(msg);
|
|
|
|
return;
|
2002-08-06 17:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type != SSH_KEYTYPE_SSH1 &&
|
2019-09-08 19:29:00 +00:00
|
|
|
type != SSH_KEYTYPE_SSH2) {
|
|
|
|
realtype = type;
|
|
|
|
type = import_target_type(type);
|
2002-08-06 17:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
comment = NULL;
|
2011-10-02 14:14:21 +00:00
|
|
|
passphrase = NULL;
|
2002-08-06 17:48:14 +00:00
|
|
|
if (realtype == SSH_KEYTYPE_SSH1)
|
2020-01-05 10:28:45 +00:00
|
|
|
needs_pass = rsa1_encrypted_f(filename, &comment);
|
2002-08-06 17:48:14 +00:00
|
|
|
else if (realtype == SSH_KEYTYPE_SSH2)
|
2020-01-05 10:28:45 +00:00
|
|
|
needs_pass = ppk_encrypted_f(filename, &comment);
|
2002-08-06 17:48:14 +00:00
|
|
|
else
|
2019-09-08 19:29:00 +00:00
|
|
|
needs_pass = import_encrypted(filename, realtype, &comment);
|
2002-08-06 17:48:14 +00:00
|
|
|
do {
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(passphrase);
|
|
|
|
passphrase = NULL;
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (needs_pass) {
|
|
|
|
int dlgret;
|
2011-10-02 14:14:21 +00:00
|
|
|
struct PassphraseProcStruct pps;
|
|
|
|
pps.passphrase = &passphrase;
|
|
|
|
pps.comment = comment;
|
2019-09-08 19:29:00 +00:00
|
|
|
dlgret = DialogBoxParam(hinst,
|
|
|
|
MAKEINTRESOURCE(210),
|
|
|
|
NULL, PassphraseProc,
|
|
|
|
(LPARAM) &pps);
|
|
|
|
if (!dlgret) {
|
|
|
|
ret = -2;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-02 14:14:21 +00:00
|
|
|
assert(passphrase != NULL);
|
2019-09-08 19:29:00 +00:00
|
|
|
} else
|
|
|
|
passphrase = dupstr("");
|
|
|
|
if (type == SSH_KEYTYPE_SSH1) {
|
|
|
|
if (realtype == type)
|
2020-01-05 10:28:45 +00:00
|
|
|
ret = rsa1_load_f(filename, &newkey1, passphrase, &errmsg);
|
2019-09-08 19:29:00 +00:00
|
|
|
else
|
|
|
|
ret = import_ssh1(filename, realtype, &newkey1,
|
2011-10-02 11:01:57 +00:00
|
|
|
passphrase, &errmsg);
|
2019-09-08 19:29:00 +00:00
|
|
|
} else {
|
|
|
|
if (realtype == type)
|
2020-01-05 10:28:45 +00:00
|
|
|
newkey2 = ppk_load_f(filename, passphrase, &errmsg);
|
2019-09-08 19:29:00 +00:00
|
|
|
else
|
|
|
|
newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
|
|
|
|
if (newkey2 == SSH2_WRONG_PASSPHRASE)
|
|
|
|
ret = -1;
|
|
|
|
else if (!newkey2)
|
|
|
|
ret = 0;
|
|
|
|
else
|
|
|
|
ret = 1;
|
|
|
|
}
|
2002-08-06 17:48:14 +00:00
|
|
|
} while (ret == -1);
|
|
|
|
if (comment)
|
2019-09-08 19:29:00 +00:00
|
|
|
sfree(comment);
|
2002-08-06 17:48:14 +00:00
|
|
|
if (ret == 0) {
|
2019-09-08 19:29:00 +00:00
|
|
|
char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
|
2020-02-02 10:00:42 +00:00
|
|
|
message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
|
2023-05-29 12:28:11 +00:00
|
|
|
false, HELPCTXID(errors_cantloadkey));
|
2019-09-08 19:29:00 +00:00
|
|
|
sfree(msg);
|
2002-08-06 17:48:14 +00:00
|
|
|
} else if (ret == 1) {
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* Now update the key controls with all the
|
|
|
|
* key data.
|
|
|
|
*/
|
2022-04-02 15:15:53 +00:00
|
|
|
update_ui_after_load(hwnd, state, passphrase, type, &newkey1, newkey2);
|
2019-09-08 19:29:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the user has imported a foreign key
|
|
|
|
* using the Load command, let them know.
|
|
|
|
* If they've used the Import command, be
|
|
|
|
* silent.
|
|
|
|
*/
|
|
|
|
if (realtype != type && !was_import_cmd) {
|
|
|
|
char msg[512];
|
|
|
|
sprintf(msg, "Successfully imported foreign key\n"
|
|
|
|
"(%s).\n"
|
|
|
|
"To use this key with PuTTY, you need to\n"
|
|
|
|
"use the \"Save private key\" command to\n"
|
|
|
|
"save it in PuTTY's own format.",
|
|
|
|
key_type_to_str(realtype));
|
|
|
|
MessageBox(NULL, msg, "PuTTYgen Notice",
|
|
|
|
MB_OK | MB_ICONINFORMATION);
|
|
|
|
}
|
2002-08-06 17:48:14 +00:00
|
|
|
}
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(passphrase);
|
2002-08-06 17:48:14 +00:00
|
|
|
}
|
|
|
|
|
2022-04-19 16:59:46 +00:00
|
|
|
void add_certificate(HWND hwnd, struct MainDlgState *state,
|
|
|
|
Filename *filename)
|
|
|
|
{
|
|
|
|
int type = key_type(filename);
|
|
|
|
if (type != SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 &&
|
|
|
|
type != SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
|
|
|
|
char *msg = dupprintf("Couldn't load certificate (%s)",
|
|
|
|
key_type_to_str(type));
|
|
|
|
message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
|
2023-05-29 12:28:11 +00:00
|
|
|
false, HELPCTXID(errors_cantloadkey));
|
2022-04-19 16:59:46 +00:00
|
|
|
sfree(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *algname = NULL;
|
|
|
|
char *comment = NULL;
|
|
|
|
const char *error = NULL;
|
|
|
|
strbuf *pub = strbuf_new();
|
|
|
|
if (!ppk_loadpub_f(filename, &algname, BinarySink_UPCAST(pub), &comment,
|
|
|
|
&error)) {
|
|
|
|
char *msg = dupprintf("Couldn't load certificate (%s)", error);
|
|
|
|
message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
|
2023-05-29 12:28:11 +00:00
|
|
|
false, HELPCTXID(errors_cantloadkey));
|
2022-04-19 16:59:46 +00:00
|
|
|
sfree(msg);
|
2022-09-07 12:52:58 +00:00
|
|
|
strbuf_free(pub);
|
2022-04-19 16:59:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sfree(comment);
|
|
|
|
|
|
|
|
const ssh_keyalg *alg = find_pubkey_alg(algname);
|
|
|
|
if (!alg) {
|
|
|
|
char *msg = dupprintf("Couldn't load certificate (unsupported "
|
|
|
|
"algorithm name '%s')", algname);
|
|
|
|
message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
|
2023-05-29 12:28:11 +00:00
|
|
|
false, HELPCTXID(errors_cantloadkey));
|
2022-04-19 16:59:46 +00:00
|
|
|
sfree(msg);
|
|
|
|
sfree(algname);
|
|
|
|
strbuf_free(pub);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sfree(algname);
|
|
|
|
|
|
|
|
/* Check the two public keys match apart from certificates */
|
|
|
|
strbuf *old_basepub = strbuf_new();
|
|
|
|
ssh_key_public_blob(ssh_key_base_key(state->ssh2key.key),
|
|
|
|
BinarySink_UPCAST(old_basepub));
|
|
|
|
|
|
|
|
ssh_key *new_pubkey = ssh_key_new_pub(alg, ptrlen_from_strbuf(pub));
|
|
|
|
strbuf *new_basepub = strbuf_new();
|
|
|
|
ssh_key_public_blob(ssh_key_base_key(new_pubkey),
|
|
|
|
BinarySink_UPCAST(new_basepub));
|
|
|
|
ssh_key_free(new_pubkey);
|
|
|
|
|
|
|
|
bool match = ptrlen_eq_ptrlen(ptrlen_from_strbuf(old_basepub),
|
|
|
|
ptrlen_from_strbuf(new_basepub));
|
|
|
|
strbuf_free(old_basepub);
|
|
|
|
strbuf_free(new_basepub);
|
|
|
|
|
|
|
|
if (!match) {
|
|
|
|
char *msg = dupprintf("Certificate is for a different public key");
|
|
|
|
message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
|
2023-05-29 12:28:11 +00:00
|
|
|
false, HELPCTXID(errors_cantloadkey));
|
2022-04-19 16:59:46 +00:00
|
|
|
sfree(msg);
|
|
|
|
strbuf_free(pub);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf *priv = strbuf_new_nm();
|
|
|
|
ssh_key_private_blob(state->ssh2key.key, BinarySink_UPCAST(priv));
|
|
|
|
ssh_key *newkey = ssh_key_new_priv(
|
|
|
|
alg, ptrlen_from_strbuf(pub), ptrlen_from_strbuf(priv));
|
|
|
|
strbuf_free(pub);
|
|
|
|
strbuf_free(priv);
|
|
|
|
|
|
|
|
if (!newkey) {
|
|
|
|
char *msg = dupprintf("Couldn't combine certificate with key");
|
|
|
|
message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
|
2023-05-29 12:28:11 +00:00
|
|
|
false, HELPCTXID(errors_cantloadkey));
|
2022-04-19 16:59:46 +00:00
|
|
|
sfree(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssh_key_free(state->ssh2key.key);
|
|
|
|
state->ssh2key.key = newkey;
|
|
|
|
|
|
|
|
update_ui_after_ssh2_pubkey_change(hwnd, state);
|
|
|
|
ui_set_state(hwnd, state, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_certificate(HWND hwnd, struct MainDlgState *state)
|
|
|
|
{
|
|
|
|
ssh_key *newkey = ssh_key_clone(ssh_key_base_key(state->ssh2key.key));
|
|
|
|
ssh_key_free(state->ssh2key.key);
|
|
|
|
state->ssh2key.key = newkey;
|
|
|
|
update_ui_after_ssh2_pubkey_change(hwnd, state);
|
|
|
|
ui_set_state(hwnd, state, 2);
|
|
|
|
}
|
|
|
|
|
2018-06-03 13:41:31 +00:00
|
|
|
static void start_generating_key(HWND hwnd, struct MainDlgState *state)
|
|
|
|
{
|
|
|
|
static const char generating_msg[] =
|
2019-09-08 19:29:00 +00:00
|
|
|
"Please wait while a key is generated...";
|
2018-06-03 13:41:31 +00:00
|
|
|
|
|
|
|
struct rsa_key_thread_params *params;
|
|
|
|
DWORD threadid;
|
|
|
|
|
|
|
|
SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
|
|
|
|
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
|
|
|
|
MAKELPARAM(0, PROGRESSRANGE));
|
|
|
|
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
|
|
|
|
|
|
|
|
params = snew(struct rsa_key_thread_params);
|
|
|
|
params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
|
|
|
|
params->dialog = hwnd;
|
|
|
|
params->key_bits = state->key_bits;
|
|
|
|
params->curve_bits = state->curve_bits;
|
|
|
|
params->keytype = state->keytype;
|
2020-02-29 06:43:28 +00:00
|
|
|
params->primepolicy = state->primepolicy;
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
params->rsa_strong = state->rsa_strong;
|
2018-06-03 13:41:31 +00:00
|
|
|
params->key = &state->key;
|
2021-04-22 17:28:35 +00:00
|
|
|
params->dsakey = &state->dsakey;
|
2018-06-03 13:41:31 +00:00
|
|
|
|
2021-07-01 17:30:00 +00:00
|
|
|
HANDLE hThread = CreateThread(NULL, 0, generate_key_thread,
|
|
|
|
params, 0, &threadid);
|
|
|
|
if (!hThread) {
|
2018-06-03 13:41:31 +00:00
|
|
|
MessageBox(hwnd, "Out of thread resources",
|
|
|
|
"Key generation error",
|
|
|
|
MB_OK | MB_ICONERROR);
|
|
|
|
sfree(params);
|
|
|
|
} else {
|
2021-07-01 17:30:00 +00:00
|
|
|
CloseHandle(hThread); /* we don't need the thread handle */
|
2018-10-29 19:50:29 +00:00
|
|
|
state->generation_thread_exists = true;
|
2018-06-03 13:41:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-30 13:41:08 +00:00
|
|
|
/*
|
|
|
|
* Dialog-box function and context structure for the 'Certificate
|
|
|
|
* info' button.
|
|
|
|
*/
|
|
|
|
struct certinfo_dialog_ctx {
|
|
|
|
SeatDialogText *text;
|
|
|
|
};
|
|
|
|
|
|
|
|
static INT_PTR CertInfoProc(HWND hwnd, UINT msg, WPARAM wParam,
|
|
|
|
LPARAM lParam, void *vctx)
|
|
|
|
{
|
|
|
|
struct certinfo_dialog_ctx *ctx = (struct certinfo_dialog_ctx *)vctx;
|
|
|
|
|
|
|
|
switch (msg) {
|
|
|
|
case WM_INITDIALOG: {
|
|
|
|
int index = 100, y = 12;
|
|
|
|
|
|
|
|
WPARAM font = SendMessage(hwnd, WM_GETFONT, 0, 0);
|
|
|
|
|
|
|
|
const char *key = NULL;
|
|
|
|
for (SeatDialogTextItem *item = ctx->text->items,
|
|
|
|
*end = item + ctx->text->nitems; item < end; item++) {
|
|
|
|
switch (item->type) {
|
|
|
|
case SDT_MORE_INFO_KEY:
|
|
|
|
key = item->text;
|
|
|
|
break;
|
|
|
|
case SDT_MORE_INFO_VALUE_SHORT:
|
|
|
|
case SDT_MORE_INFO_VALUE_BLOB: {
|
|
|
|
RECT rk, rv;
|
|
|
|
DWORD editstyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
|
|
|
|
ES_AUTOHSCROLL | ES_READONLY;
|
|
|
|
if (item->type == SDT_MORE_INFO_VALUE_BLOB) {
|
|
|
|
rk.left = 12;
|
2022-10-20 22:25:34 +00:00
|
|
|
rk.right = 286;
|
2022-07-30 13:41:08 +00:00
|
|
|
rk.top = y;
|
|
|
|
rk.bottom = 8;
|
|
|
|
y += 10;
|
|
|
|
|
|
|
|
editstyle |= ES_MULTILINE;
|
|
|
|
rv.left = 12;
|
2022-10-20 22:25:34 +00:00
|
|
|
rv.right = 286;
|
2022-07-30 13:41:08 +00:00
|
|
|
rv.top = y;
|
|
|
|
rv.bottom = 64;
|
|
|
|
y += 68;
|
|
|
|
} else {
|
|
|
|
rk.left = 12;
|
|
|
|
rk.right = 130;
|
|
|
|
rk.top = y+2;
|
|
|
|
rk.bottom = 8;
|
|
|
|
|
|
|
|
rv.left = 150;
|
2022-10-20 22:25:34 +00:00
|
|
|
rv.right = 298;
|
2022-07-30 13:41:08 +00:00
|
|
|
rv.top = y;
|
|
|
|
rv.bottom = 12;
|
|
|
|
|
|
|
|
y += 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
MapDialogRect(hwnd, &rk);
|
|
|
|
HWND ctl = CreateWindowEx(
|
|
|
|
0, "STATIC", key, WS_CHILD | WS_VISIBLE,
|
|
|
|
rk.left, rk.top, rk.right, rk.bottom,
|
|
|
|
hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
|
|
|
|
SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
|
|
|
|
|
|
|
|
MapDialogRect(hwnd, &rv);
|
|
|
|
ctl = CreateWindowEx(
|
|
|
|
WS_EX_CLIENTEDGE, "EDIT", item->text, editstyle,
|
|
|
|
rv.left, rv.top, rv.right, rv.bottom,
|
|
|
|
hwnd, (HMENU)(ULONG_PTR)index++, hinst, NULL);
|
|
|
|
SendMessage(ctl, WM_SETFONT, font, MAKELPARAM(true, 0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now resize the overall window, and move the Close button at
|
|
|
|
* the bottom.
|
|
|
|
*/
|
|
|
|
RECT r;
|
|
|
|
r.left = 176;
|
|
|
|
r.top = y + 10;
|
|
|
|
r.right = r.bottom = 0;
|
|
|
|
MapDialogRect(hwnd, &r);
|
|
|
|
HWND ctl = GetDlgItem(hwnd, IDOK);
|
|
|
|
SetWindowPos(ctl, NULL, r.left, r.top, 0, 0,
|
2022-08-03 19:48:46 +00:00
|
|
|
SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER);
|
2022-07-30 13:41:08 +00:00
|
|
|
|
|
|
|
r.left = r.top = r.right = 0;
|
|
|
|
r.bottom = 300;
|
|
|
|
MapDialogRect(hwnd, &r);
|
|
|
|
int oldheight = r.bottom;
|
|
|
|
|
|
|
|
r.left = r.top = r.right = 0;
|
|
|
|
r.bottom = y + 30;
|
|
|
|
MapDialogRect(hwnd, &r);
|
|
|
|
int newheight = r.bottom;
|
|
|
|
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
|
|
|
|
|
|
SetWindowPos(hwnd, NULL, 0, 0, r.right - r.left,
|
|
|
|
r.bottom - r.top + newheight - oldheight,
|
|
|
|
SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
|
|
|
|
|
|
|
|
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
|
|
ShinyEndDialog(hwnd, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
case WM_CLOSE:
|
|
|
|
ShinyEndDialog(hwnd, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2000-10-19 15:43:08 +00:00
|
|
|
/*
|
|
|
|
* Dialog-box function for the main PuTTYgen dialog box.
|
|
|
|
*/
|
2015-08-11 12:22:09 +00:00
|
|
|
static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
|
2022-08-03 19:48:46 +00:00
|
|
|
WPARAM wParam, LPARAM lParam)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2022-04-02 15:18:08 +00:00
|
|
|
const int DEMO_SCREENSHOT_TIMER_ID = 1230;
|
2000-10-19 15:43:08 +00:00
|
|
|
static const char entropy_msg[] =
|
2019-09-08 19:29:00 +00:00
|
|
|
"Please generate some randomness by moving the mouse over the blank area.";
|
2000-10-19 15:43:08 +00:00
|
|
|
struct MainDlgState *state;
|
|
|
|
|
|
|
|
switch (msg) {
|
|
|
|
case WM_INITDIALOG:
|
2006-12-17 11:16:07 +00:00
|
|
|
if (has_help())
|
2005-05-21 14:16:43 +00:00
|
|
|
SetWindowLongPtr(hwnd, GWL_EXSTYLE,
|
2019-09-08 19:29:00 +00:00
|
|
|
GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
|
|
|
|
WS_EX_CONTEXTHELP);
|
2001-12-12 18:45:56 +00:00
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* If we add a Help button, this is where we destroy it
|
|
|
|
* if the help file isn't present.
|
|
|
|
*/
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
|
|
|
|
(LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
|
|
|
|
|
|
|
|
state = snew(struct MainDlgState);
|
|
|
|
state->generation_thread_exists = false;
|
|
|
|
state->entropy = NULL;
|
|
|
|
state->key_exists = false;
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
|
|
|
|
{
|
|
|
|
HMENU menu, menu1;
|
|
|
|
|
|
|
|
menu = CreateMenu();
|
|
|
|
|
|
|
|
menu1 = CreateMenu();
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
|
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
|
|
|
|
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
|
|
|
|
state->filemenu = menu1;
|
|
|
|
|
|
|
|
menu1 = CreateMenu();
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
|
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
2022-04-19 16:59:46 +00:00
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_ADDCERT,
|
|
|
|
"Add &certificate to key");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_REMCERT,
|
|
|
|
"Remove certificate from key");
|
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
2019-09-08 19:29:00 +00:00
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
|
2014-11-01 09:45:20 +00:00
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
|
2020-03-02 07:05:19 +00:00
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2EDDSA, "SSH-2 EdD&SA key");
|
2020-02-29 06:43:28 +00:00
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_PROB,
|
|
|
|
"Use probable primes (fast)");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_SIMPLE,
|
|
|
|
"Use proven primes (slower)");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_COMPLEX,
|
|
|
|
"Use proven primes with even distribution (slowest)");
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_RSA_STRONG,
|
|
|
|
"Use \"strong\" primes as RSA key factors");
|
2021-02-22 20:15:11 +00:00
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_PPK_PARAMS,
|
|
|
|
"Parameters for saving key files...");
|
2021-03-13 09:58:14 +00:00
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_SHA256,
|
|
|
|
"Show fingerprint as SHA256");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_MD5,
|
|
|
|
"Show fingerprint as MD5");
|
2019-09-08 19:29:00 +00:00
|
|
|
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
|
|
|
|
state->keymenu = menu1;
|
|
|
|
|
|
|
|
menu1 = CreateMenu();
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
|
|
|
|
AppendMenu(menu1, MF_SEPARATOR, 0, 0);
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
|
|
|
|
"Export &OpenSSH key");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
|
|
|
|
"Export &OpenSSH key (force new file format)");
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
|
|
|
|
"Export &ssh.com key");
|
|
|
|
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
|
|
|
|
"Con&versions");
|
|
|
|
state->cvtmenu = menu1;
|
|
|
|
|
|
|
|
menu1 = CreateMenu();
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
|
|
|
|
if (has_help())
|
|
|
|
AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
|
|
|
|
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
|
|
|
|
|
|
|
|
SetMenu(hwnd, menu);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Centre the window.
|
|
|
|
*/
|
|
|
|
{ /* centre the window */
|
|
|
|
RECT rs, rd;
|
|
|
|
HWND hw;
|
|
|
|
|
|
|
|
hw = GetDesktopWindow();
|
|
|
|
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
|
|
|
|
MoveWindow(hwnd,
|
|
|
|
(rs.right + rs.left + rd.left - rd.right) / 2,
|
|
|
|
(rs.bottom + rs.top + rd.top - rd.bottom) / 2,
|
|
|
|
rd.right - rd.left, rd.bottom - rd.top, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
struct ctlpos cp, cp2;
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
int ymax;
|
2000-10-19 15:43:08 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
/* Accelerators used: acglops1rbvde */
|
|
|
|
|
|
|
|
ctlposinit(&cp, hwnd, 4, 4, 4);
|
|
|
|
beginbox(&cp, "Key", IDC_BOX_KEY);
|
|
|
|
cp2 = cp;
|
|
|
|
statictext(&cp2, "No key.", 1, IDC_NOKEY);
|
|
|
|
cp2 = cp;
|
|
|
|
statictext(&cp2, "", 1, IDC_GENERATING);
|
|
|
|
progressbar(&cp2, IDC_PROGRESS);
|
2022-07-30 13:41:08 +00:00
|
|
|
cp2 = cp;
|
|
|
|
bigeditctrl(&cp2, NULL, -1, IDC_CERTSTATIC, 3);
|
|
|
|
{
|
|
|
|
HWND child = GetDlgItem(hwnd, IDC_CERTSTATIC);
|
|
|
|
LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE);
|
|
|
|
style &= ~WS_VSCROLL;
|
|
|
|
SetWindowLongPtr(child, GWL_STYLE, style);
|
|
|
|
SendMessage(child, EM_SETREADONLY, true, 0);
|
|
|
|
}
|
|
|
|
MakeDlgItemBorderless(hwnd, IDC_CERTSTATIC);
|
|
|
|
cp2.xoff = cp2.width = cp2.width / 3;
|
|
|
|
button(&cp2, "Certificate info...", IDC_CERTMOREINFO, false);
|
2019-09-08 19:29:00 +00:00
|
|
|
bigeditctrl(&cp,
|
|
|
|
"&Public key for pasting into authorized_keys file:",
|
|
|
|
IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
|
|
|
|
SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
|
|
|
|
staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
|
2021-03-13 09:58:14 +00:00
|
|
|
IDC_FINGERPRINT, 82);
|
2019-09-08 19:29:00 +00:00
|
|
|
SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
|
|
|
|
0);
|
|
|
|
staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
|
2021-03-13 09:58:14 +00:00
|
|
|
IDC_COMMENTEDIT, 82);
|
2019-09-08 19:29:00 +00:00
|
|
|
staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
|
2021-03-13 09:58:14 +00:00
|
|
|
IDC_PASSPHRASE1EDIT, 82);
|
2019-09-08 19:29:00 +00:00
|
|
|
staticpassedit(&cp, "C&onfirm passphrase:",
|
2021-03-13 09:58:14 +00:00
|
|
|
IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 82);
|
2019-09-08 19:29:00 +00:00
|
|
|
endbox(&cp);
|
|
|
|
beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
|
|
|
|
staticbtn(&cp, "Generate a public/private key pair",
|
|
|
|
IDC_GENSTATIC, "&Generate", IDC_GENERATE);
|
|
|
|
staticbtn(&cp, "Load an existing private key file",
|
|
|
|
IDC_LOADSTATIC, "&Load", IDC_LOAD);
|
|
|
|
static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
|
|
|
|
"Save p&ublic key", IDC_SAVEPUB,
|
|
|
|
"&Save private key", IDC_SAVE);
|
|
|
|
endbox(&cp);
|
|
|
|
beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
|
|
|
|
radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
|
|
|
|
"&RSA", IDC_KEYSSH2RSA,
|
2015-05-14 12:19:15 +00:00
|
|
|
"&DSA", IDC_KEYSSH2DSA,
|
|
|
|
"&ECDSA", IDC_KEYSSH2ECDSA,
|
2020-03-02 07:05:19 +00:00
|
|
|
"EdD&SA", IDC_KEYSSH2EDDSA,
|
2019-09-08 19:29:00 +00:00
|
|
|
"SSH-&1 (RSA)", IDC_KEYSSH1,
|
2015-05-14 12:19:15 +00:00
|
|
|
NULL);
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
cp2 = cp;
|
2019-09-08 19:29:00 +00:00
|
|
|
staticedit(&cp2, "Number of &bits in a generated key:",
|
|
|
|
IDC_BITSSTATIC, IDC_BITS, 20);
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
ymax = cp2.ypos;
|
|
|
|
cp2 = cp;
|
2019-09-08 19:29:00 +00:00
|
|
|
staticddl(&cp2, "Cur&ve to use for generating this key:",
|
2020-03-02 07:05:19 +00:00
|
|
|
IDC_ECCURVESTATIC, IDC_ECCURVE, 30);
|
|
|
|
SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_RESETCONTENT, 0, 0);
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
{
|
|
|
|
int i, bits;
|
|
|
|
const struct ec_curve *curve;
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
const ssh_keyalg *alg;
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
|
|
|
|
for (i = 0; i < n_ec_nist_curve_lengths; i++) {
|
|
|
|
bits = ec_nist_curve_lengths[i];
|
|
|
|
ec_nist_alg_and_curve_by_bits(bits, &curve, &alg);
|
2020-03-02 07:05:19 +00:00
|
|
|
SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_ADDSTRING, 0,
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
(LPARAM)curve->textname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
|
|
|
|
cp2 = cp;
|
2020-03-02 07:05:19 +00:00
|
|
|
staticddl(&cp2, "Cur&ve to use for generating this key:",
|
|
|
|
IDC_EDCURVESTATIC, IDC_EDCURVE, 30);
|
|
|
|
SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_RESETCONTENT, 0, 0);
|
|
|
|
{
|
|
|
|
int i, bits;
|
|
|
|
const struct ec_curve *curve;
|
|
|
|
const ssh_keyalg *alg;
|
|
|
|
|
|
|
|
for (i = 0; i < n_ec_ed_curve_lengths; i++) {
|
|
|
|
bits = ec_ed_curve_lengths[i];
|
|
|
|
ec_ed_alg_and_curve_by_bits(bits, &curve, &alg);
|
|
|
|
char *desc = dupprintf("%s (%d bits)",
|
|
|
|
curve->textname, bits);
|
|
|
|
SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_ADDSTRING, 0,
|
|
|
|
(LPARAM)desc);
|
|
|
|
sfree(desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
|
|
|
|
cp2 = cp;
|
2019-09-08 19:29:00 +00:00
|
|
|
statictext(&cp2, "(nothing to configure for this key type)",
|
|
|
|
1, IDC_NOTHINGSTATIC);
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
|
|
|
|
cp.ypos = ymax;
|
2019-09-08 19:29:00 +00:00
|
|
|
endbox(&cp);
|
|
|
|
}
|
2022-01-15 18:30:34 +00:00
|
|
|
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);
|
2020-03-02 07:05:19 +00:00
|
|
|
SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_SETCURSEL,
|
2022-01-15 18:30:34 +00:00
|
|
|
params->eccurve_index, 0);
|
2020-03-02 07:05:19 +00:00
|
|
|
SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_SETCURSEL,
|
2022-01-15 18:30:34 +00:00
|
|
|
params->edcurve_index, 0);
|
2000-10-20 10:07:53 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
/*
|
|
|
|
* Initially, hide the progress bar and the key display,
|
|
|
|
* and show the no-key display. Also disable the Save
|
|
|
|
* buttons, because with no key we obviously can't save
|
|
|
|
* anything.
|
|
|
|
*/
|
|
|
|
ui_set_state(hwnd, state, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load a key file if one was provided on the command line.
|
|
|
|
*/
|
|
|
|
if (cmdline_keyfile) {
|
2024-09-24 16:50:19 +00:00
|
|
|
load_key_file(hwnd, state, cmdline_keyfile, false);
|
2022-04-02 15:18:08 +00:00
|
|
|
} else if (cmdline_demo_keystr.ptr) {
|
|
|
|
BinarySource src[1];
|
|
|
|
BinarySource_BARE_INIT_PL(src, cmdline_demo_keystr);
|
|
|
|
const char *errmsg;
|
|
|
|
ssh2_userkey *k = ppk_load_s(src, NULL, &errmsg);
|
|
|
|
assert(!errmsg);
|
|
|
|
|
|
|
|
update_ui_after_load(hwnd, state, "demo passphrase",
|
|
|
|
SSH_KEYTYPE_SSH2, NULL, k);
|
|
|
|
|
|
|
|
SetTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID, TICKSPERSEC, NULL);
|
2013-07-22 19:55:55 +00:00
|
|
|
}
|
2002-08-06 17:48:14 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
return 1;
|
2022-04-02 15:18:08 +00:00
|
|
|
case WM_TIMER:
|
|
|
|
if ((UINT_PTR)wParam == DEMO_SCREENSHOT_TIMER_ID) {
|
|
|
|
KillTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID);
|
2022-05-08 07:51:45 +00:00
|
|
|
char *err = save_screenshot(hwnd, demo_screenshot_filename);
|
|
|
|
if (err) {
|
2022-04-02 15:18:08 +00:00
|
|
|
MessageBox(hwnd, err, "Demo screenshot failure",
|
|
|
|
MB_OK | MB_ICONERROR);
|
2022-05-08 07:51:45 +00:00
|
|
|
sfree(err);
|
|
|
|
}
|
2022-04-02 15:18:08 +00:00
|
|
|
EndDialog(hwnd, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_MOUSEMOVE:
|
2019-09-08 19:29:00 +00:00
|
|
|
state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
2022-01-08 11:23:46 +00:00
|
|
|
if (state->entropy && state->entropy_got < state->entropy_required) {
|
Windows PuTTYgen: rate-limit entropy counter increment.
Some pointing devices (e.g. gaming mice) can be set to send
mouse-movement events at an extremely high sample rate like 1kHz. This
apparently translates into Windows genuinely sending WM_MOUSEMOVE
messages at that rate. So if you're using one of those mice, then
PuTTYgen's mouse-based entropy collection system will fill its buffer
almost immediately, and give you no perceptible time to actually wave
the mouse around.
I think that in that situation, there's also likely to be a strong
correlation between the contents of successive movement events,
because human-controlled mouse movements aren't fractals - the more
you zoom in on a little piece of one, the more it starts to look more
and more like something moving in a straight line at constant speed,
because the deviations from that happen on a larger time scale than
you're seeing.
So this commit adds a rate limit, not on the _collection_ of the data
(we'll still take all the bits we can get, thanks!), but on the rate
at which we increment the _counter_ for how much entropy we think
we've collected so far. That way, the user still has to spend time
wiggling the mouse back and forth in a way that varies with muscle
motions and introduces randomness.
2022-01-08 12:09:36 +00:00
|
|
|
ULONG msgtime = GetMessageTime();
|
2022-01-08 11:20:27 +00:00
|
|
|
put_uint32(state->entropy, lParam);
|
Windows PuTTYgen: rate-limit entropy counter increment.
Some pointing devices (e.g. gaming mice) can be set to send
mouse-movement events at an extremely high sample rate like 1kHz. This
apparently translates into Windows genuinely sending WM_MOUSEMOVE
messages at that rate. So if you're using one of those mice, then
PuTTYgen's mouse-based entropy collection system will fill its buffer
almost immediately, and give you no perceptible time to actually wave
the mouse around.
I think that in that situation, there's also likely to be a strong
correlation between the contents of successive movement events,
because human-controlled mouse movements aren't fractals - the more
you zoom in on a little piece of one, the more it starts to look more
and more like something moving in a straight line at constant speed,
because the deviations from that happen on a larger time scale than
you're seeing.
So this commit adds a rate limit, not on the _collection_ of the data
(we'll still take all the bits we can get, thanks!), but on the rate
at which we increment the _counter_ for how much entropy we think
we've collected so far. That way, the user still has to spend time
wiggling the mouse back and forth in a way that varies with muscle
motions and introduces randomness.
2022-01-08 12:09:36 +00:00
|
|
|
put_uint32(state->entropy, msgtime);
|
|
|
|
if (msgtime - state->entropy_prev_msgtime > ENTROPY_RATE_LIMIT) {
|
|
|
|
state->entropy_got += 2;
|
|
|
|
state->entropy_prev_msgtime = msgtime;
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
|
|
|
|
state->entropy_got, 0);
|
|
|
|
if (state->entropy_got >= state->entropy_required) {
|
|
|
|
/*
|
|
|
|
* Seed the entropy pool
|
|
|
|
*/
|
2022-01-08 11:20:27 +00:00
|
|
|
random_reseed(ptrlen_from_strbuf(state->entropy));
|
|
|
|
strbuf_free(state->entropy);
|
2022-01-08 11:23:46 +00:00
|
|
|
state->entropy = NULL;
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2018-06-03 13:41:31 +00:00
|
|
|
start_generating_key(hwnd, state);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_COMMAND:
|
2019-09-08 19:29:00 +00:00
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDC_KEYSSH1:
|
|
|
|
case IDC_KEYSSH2RSA:
|
|
|
|
case IDC_KEYSSH2DSA:
|
2014-11-01 09:45:20 +00:00
|
|
|
case IDC_KEYSSH2ECDSA:
|
2020-03-02 07:05:19 +00:00
|
|
|
case IDC_KEYSSH2EDDSA: {
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
state = (struct MainDlgState *)
|
|
|
|
GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
ui_set_key_type(hwnd, state, LOWORD(wParam));
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
2020-02-29 06:43:28 +00:00
|
|
|
case IDC_PRIMEGEN_PROB:
|
|
|
|
case IDC_PRIMEGEN_MAURER_SIMPLE:
|
|
|
|
case IDC_PRIMEGEN_MAURER_COMPLEX: {
|
|
|
|
state = (struct MainDlgState *)
|
|
|
|
GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
ui_set_primepolicy(hwnd, state, LOWORD(wParam));
|
|
|
|
break;
|
|
|
|
}
|
2021-03-13 09:58:14 +00:00
|
|
|
case IDC_FPTYPE_SHA256:
|
|
|
|
case IDC_FPTYPE_MD5: {
|
|
|
|
state = (struct MainDlgState *)
|
|
|
|
GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
ui_set_fptype(hwnd, state, LOWORD(wParam));
|
|
|
|
break;
|
|
|
|
}
|
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography,
is a prime p such that each of p-1 and p+1 has a large prime factor,
and that the large factor q of p-1 is such that q-1 in turn _also_ has
a large prime factor.
HoAC says that making your RSA key using primes of this form defeats
some factoring algorithms - but there are other faster algorithms to
which it makes no difference. So this is probably not a useful
precaution in practice. However, it has been recommended in the past
by some official standards, and it's easy to implement given the new
general facility in PrimeCandidateSource that lets you ask for your
prime to satisfy an arbitrary modular congruence. (And HoAC also says
there's no particular reason _not_ to use strong primes.) So I provide
it as an option, just in case anyone wants to select it.
The change to the key generation algorithm is entirely in sshrsag.c,
and is neatly independent of the prime-generation system in use. If
you're using Maurer provable prime generation, then the known factor q
of p-1 can be used to help certify p, and the one for q-1 to help with
q in turn; if you switch to probabilistic prime generation then you
still get an RSA key with the right structure, except that every time
the definition says 'prime factor' you just append '(probably)'.
(The probabilistic version of this procedure is described as 'Gordon's
algorithm' in HoAC section 4.4.2.)
2020-03-02 06:52:09 +00:00
|
|
|
case IDC_RSA_STRONG: {
|
|
|
|
state = (struct MainDlgState *)
|
|
|
|
GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
ui_set_rsa_strong(hwnd, state, !state->rsa_strong);
|
|
|
|
break;
|
|
|
|
}
|
2021-02-22 20:15:11 +00:00
|
|
|
case IDC_PPK_PARAMS: {
|
|
|
|
struct PPKParams pp[1];
|
|
|
|
pp->params = save_params;
|
|
|
|
if (pp->params.argon2_passes_auto) {
|
|
|
|
pp->time_ms = pp->params.argon2_milliseconds;
|
|
|
|
pp->time_passes = 13;
|
|
|
|
} else {
|
|
|
|
pp->time_ms = 100;
|
|
|
|
pp->time_passes = pp->params.argon2_passes;
|
|
|
|
}
|
|
|
|
int dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(215),
|
|
|
|
NULL, PPKParamsProc, (LPARAM)pp);
|
|
|
|
if (dlgret) {
|
|
|
|
if (pp->params.argon2_passes_auto) {
|
|
|
|
pp->params.argon2_milliseconds = pp->time_ms;
|
|
|
|
} else {
|
|
|
|
pp->params.argon2_passes = pp->time_passes;
|
|
|
|
}
|
|
|
|
save_params = pp->params;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
case IDC_QUIT:
|
|
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
|
|
|
break;
|
|
|
|
case IDC_COMMENTEDIT:
|
|
|
|
if (HIWORD(wParam) == EN_CHANGE) {
|
|
|
|
state = (struct MainDlgState *)
|
|
|
|
GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (state->key_exists) {
|
|
|
|
HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
|
|
|
|
int len = GetWindowTextLength(editctl);
|
|
|
|
if (*state->commentptr)
|
|
|
|
sfree(*state->commentptr);
|
|
|
|
*state->commentptr = snewn(len + 1, char);
|
|
|
|
GetWindowText(editctl, *state->commentptr, len + 1);
|
|
|
|
if (state->ssh2) {
|
2022-07-30 13:41:08 +00:00
|
|
|
setupbigedit2(hwnd, &state->ssh2key);
|
2019-09-08 19:29:00 +00:00
|
|
|
} else {
|
2022-07-30 13:41:08 +00:00
|
|
|
setupbigedit1(hwnd, &state->key);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_ABOUT:
|
|
|
|
EnableWindow(hwnd, 0);
|
|
|
|
DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
|
|
|
|
EnableWindow(hwnd, 1);
|
|
|
|
SetActiveWindow(hwnd);
|
|
|
|
return 0;
|
|
|
|
case IDC_GIVEHELP:
|
2002-05-11 16:45:29 +00:00
|
|
|
if (HIWORD(wParam) == BN_CLICKED ||
|
|
|
|
HIWORD(wParam) == BN_DOUBLECLICKED) {
|
2019-09-08 19:29:00 +00:00
|
|
|
launch_help(hwnd, WINHELP_CTX_puttygen_general);
|
2002-05-11 16:45:29 +00:00
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
return 0;
|
|
|
|
case IDC_GENERATE:
|
2003-03-22 09:22:52 +00:00
|
|
|
if (HIWORD(wParam) != BN_CLICKED &&
|
|
|
|
HIWORD(wParam) != BN_DOUBLECLICKED)
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
|
|
|
state =
|
|
|
|
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (!state->generation_thread_exists) {
|
2018-06-03 13:41:31 +00:00
|
|
|
unsigned raw_entropy_required;
|
|
|
|
unsigned char *raw_entropy_buf;
|
2019-09-08 19:29:00 +00:00
|
|
|
BOOL ok;
|
2020-03-02 07:05:19 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, false);
|
|
|
|
if (!ok)
|
|
|
|
state->key_bits = DEFAULT_KEY_BITS;
|
2020-03-02 07:05:19 +00:00
|
|
|
state->ssh2 = true;
|
|
|
|
|
|
|
|
if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1)) {
|
|
|
|
state->ssh2 = false;
|
|
|
|
state->keytype = RSA;
|
|
|
|
} else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA)) {
|
|
|
|
state->keytype = RSA;
|
|
|
|
} else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
|
|
|
|
state->keytype = DSA;
|
|
|
|
} else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
|
|
|
|
state->keytype = ECDSA;
|
|
|
|
int curveindex = SendDlgItemMessage(hwnd, IDC_ECCURVE,
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
assert(curveindex >= 0);
|
|
|
|
assert(curveindex < n_ec_nist_curve_lengths);
|
|
|
|
state->curve_bits = ec_nist_curve_lengths[curveindex];
|
2020-03-02 07:05:19 +00:00
|
|
|
} else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) {
|
|
|
|
state->keytype = EDDSA;
|
|
|
|
int curveindex = SendDlgItemMessage(hwnd, IDC_EDCURVE,
|
|
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
assert(curveindex >= 0);
|
|
|
|
assert(curveindex < n_ec_ed_curve_lengths);
|
|
|
|
state->curve_bits = ec_ed_curve_lengths[curveindex];
|
|
|
|
} else {
|
|
|
|
/* Somehow, no button was checked */
|
|
|
|
break;
|
2014-11-01 09:14:19 +00:00
|
|
|
}
|
2016-04-02 07:00:37 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if ((state->keytype == RSA || state->keytype == DSA) &&
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
state->key_bits < 256) {
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
char *message = dupprintf(
|
|
|
|
"PuTTYgen will not generate a key smaller than 256"
|
|
|
|
" bits.\nKey length reset to default %d. Continue?",
|
|
|
|
DEFAULT_KEY_BITS);
|
2019-09-08 19:29:00 +00:00
|
|
|
int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
|
|
|
|
MB_ICONWARNING | MB_OKCANCEL);
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
sfree(message);
|
2019-09-08 19:29:00 +00:00
|
|
|
if (ret != IDOK)
|
|
|
|
break;
|
|
|
|
state->key_bits = DEFAULT_KEY_BITS;
|
|
|
|
SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false);
|
|
|
|
} else if ((state->keytype == RSA || state->keytype == DSA) &&
|
2016-04-02 07:00:37 +00:00
|
|
|
state->key_bits < DEFAULT_KEY_BITS) {
|
Formatting: standardise on "func(\n", not "func\n(".
If the function name (or expression) in a function call or declaration
is itself so long that even the first argument doesn't fit after it on
the same line, or if that would leave so little space that it would be
silly to try to wrap all the run-on lines into a tall thin column,
then I used to do this
ludicrously_long_function_name
(arg1, arg2, arg3);
and now prefer this
ludicrously_long_function_name(
arg1, arg2, arg3);
I picked up the habit from Python, where the latter idiom is required
by Python's syntactic significance of newlines (you can write the
former if you use a backslash-continuation, but pretty much everyone
seems to agree that that's much uglier). But I've found it works well
in C as well: it makes it more obvious that the previous line is
incomplete, it gives you a tiny bit more space to wrap the following
lines into (the old idiom indents the _third_ line one space beyond
the second), and I generally turn out to agree with the knock-on
indentation decisions made by at least Emacs if you do it in the
middle of a complex expression. Plus, of course, using the _same_
idiom between C and Python means less state-switching.
So, while I'm making annoying indentation changes in general, this
seems like a good time to dig out all the cases of the old idiom in
this code, and switch them over to the new.
2022-08-03 19:48:46 +00:00
|
|
|
char *message = dupprintf(
|
|
|
|
"Keys shorter than %d bits are not recommended. "
|
|
|
|
"Really generate this key?", DEFAULT_KEY_BITS);
|
2019-09-08 19:29:00 +00:00
|
|
|
int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
|
|
|
|
MB_ICONWARNING | MB_OKCANCEL);
|
2016-04-02 07:00:37 +00:00
|
|
|
sfree(message);
|
2019-09-08 19:29:00 +00:00
|
|
|
if (ret != IDOK)
|
|
|
|
break;
|
2016-04-02 07:00:37 +00:00
|
|
|
}
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (state->keytype == RSA || state->keytype == DSA)
|
2018-06-03 13:41:31 +00:00
|
|
|
raw_entropy_required = (state->key_bits / 2) * 2;
|
2020-03-02 07:05:19 +00:00
|
|
|
else if (state->keytype == ECDSA || state->keytype == EDDSA)
|
2018-06-03 13:41:31 +00:00
|
|
|
raw_entropy_required = (state->curve_bits / 2) * 2;
|
Polish up the PuTTYgen user interface for ECC key types.
Jacob pointed out that a free-text field for entering a key size in
bits is all very well for key types where we actually _can_ generate a
key to a size of your choice, but less useful for key types where
there are only three (or one) legal values for the field, especially
if we don't _say_ what they are.
So I've revamped the UI a bit: now, in ECDSA mode, you get a dropdown
list selector showing the available elliptic curves (and they're even
named, rather than just given by bit count), and in ED25519 mode even
that disappears. The curve selector for ECDSA and the bits selector
for RSA/DSA are independent controls, so each one remembers its last
known value even while temporarily hidden in favour of the other.
The actual generation function still expects a bit count rather than
an actual curve or algorithm ID, so the easiest way to actually
arrange to populate the drop-down list was to have an array of bit
counts exposed by sshecc.c. That's a bit ugly, but there we go.
One small functional change: if you enter an absurdly low value into
the RSA/DSA bit count box (under 256), PuTTYgen used to give a warning
and reset it to 256. Now it resets it to the default key length of
2048, basically because I was touching that code anyway to change a
variable name and just couldn't bring myself to leave it in a state
where it intentionally chose such an utterly useless key size. Of
course this doesn't prevent generation of 256-bit keys if someone
still really wants one - it just means they don't get one selected as
the result of a typo.
2016-03-25 07:53:06 +00:00
|
|
|
else
|
2020-03-02 07:05:19 +00:00
|
|
|
unreachable("we must have initialised keytype by now");
|
2018-06-03 13:41:31 +00:00
|
|
|
|
2019-02-10 13:43:14 +00:00
|
|
|
/* Bound the entropy collection above by the amount of
|
|
|
|
* data we can actually fit into the PRNG. Any more
|
|
|
|
* than that and it's doing no more good. */
|
|
|
|
if (raw_entropy_required > random_seed_bits())
|
|
|
|
raw_entropy_required = random_seed_bits();
|
|
|
|
|
2018-06-03 13:41:31 +00:00
|
|
|
raw_entropy_buf = snewn(raw_entropy_required, unsigned char);
|
|
|
|
if (win_read_random(raw_entropy_buf, raw_entropy_required)) {
|
|
|
|
/*
|
2019-02-10 13:38:15 +00:00
|
|
|
* If we can get entropy from CryptGenRandom, use
|
|
|
|
* it. But CryptGenRandom isn't a kernel-level
|
|
|
|
* CPRNG (according to Wikipedia), and papers have
|
|
|
|
* been published cryptanalysing it. So we'll
|
|
|
|
* still do manual entropy collection; we'll just
|
|
|
|
* do it _as well_ as this.
|
2018-06-03 13:41:31 +00:00
|
|
|
*/
|
Replace PuTTY's PRNG with a Fortuna-like system.
This tears out the entire previous random-pool system in sshrand.c. In
its place is a system pretty close to Ferguson and Schneier's
'Fortuna' generator, with the main difference being that I use SHA-256
instead of AES for the generation side of the system (rationale given
in comment).
The PRNG implementation lives in sshprng.c, and defines a self-
contained data type with no state stored outside the object, so you
can instantiate however many of them you like. The old sshrand.c still
exists, but in place of the previous random pool system, it's just
become a client of sshprng.c, whose job is to hold a single global
instance of the PRNG type, and manage its reference count, save file,
noise-collection timers and similar administrative business.
Advantages of this change include:
- Fortuna is designed with a more varied threat model in mind than my
old home-grown random pool. For example, after any request for
random numbers, it automatically re-seeds itself, so that if the
state of the PRNG should be leaked, it won't give enough
information to find out what past outputs _were_.
- The PRNG type can be instantiated with any hash function; the
instance used by the main tools is based on SHA-256, an improvement
on the old pool's use of SHA-1.
- The new PRNG only uses the completely standard interface to the
hash function API, instead of having to have privileged access to
the internal SHA-1 block transform function. This will make it
easier to revamp the hash code in general, and also it means that
hardware-accelerated versions of SHA-256 will automatically be used
for the PRNG as well as for everything else.
- The new PRNG can be _tested_! Because it has an actual (if not
quite explicit) specification for exactly what the output numbers
_ought_ to be derived from the hashes of, I can (and have) put
tests in cryptsuite that ensure the output really is being derived
in the way I think it is. The old pool could have been returning
any old nonsense and it would have been very hard to tell for sure.
2019-01-22 22:42:41 +00:00
|
|
|
random_reseed(
|
|
|
|
make_ptrlen(raw_entropy_buf, raw_entropy_required));
|
2018-06-03 13:41:31 +00:00
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2019-02-10 13:38:15 +00:00
|
|
|
/*
|
|
|
|
* Manual entropy input, by making the user wave the
|
|
|
|
* mouse over the window a lot.
|
|
|
|
*
|
|
|
|
* My brief statistical tests on mouse movements
|
|
|
|
* suggest that there are about 2.5 bits of randomness
|
|
|
|
* in the x position, 2.5 in the y position, and 1.7
|
|
|
|
* in the message time, making 5.7 bits of
|
|
|
|
* unpredictability per mouse movement. However, other
|
|
|
|
* people have told me it's far less than that, so I'm
|
|
|
|
* going to be stupidly cautious and knock that down
|
|
|
|
* to a nice round 2. With this method, we require two
|
|
|
|
* words per mouse movement, so with 2 bits per mouse
|
|
|
|
* movement we expect 2 bits every 2 words, i.e. the
|
|
|
|
* number of _words_ of mouse data we want to collect
|
|
|
|
* is just the same as the number of _bits_ of entropy
|
|
|
|
* we want.
|
|
|
|
*/
|
|
|
|
state->entropy_required = raw_entropy_required;
|
|
|
|
|
|
|
|
ui_set_state(hwnd, state, 1);
|
|
|
|
SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
|
|
|
|
state->key_exists = false;
|
|
|
|
|
|
|
|
state->entropy_got = 0;
|
2022-01-08 11:20:27 +00:00
|
|
|
state->entropy = strbuf_new_nm();
|
Windows PuTTYgen: rate-limit entropy counter increment.
Some pointing devices (e.g. gaming mice) can be set to send
mouse-movement events at an extremely high sample rate like 1kHz. This
apparently translates into Windows genuinely sending WM_MOUSEMOVE
messages at that rate. So if you're using one of those mice, then
PuTTYgen's mouse-based entropy collection system will fill its buffer
almost immediately, and give you no perceptible time to actually wave
the mouse around.
I think that in that situation, there's also likely to be a strong
correlation between the contents of successive movement events,
because human-controlled mouse movements aren't fractals - the more
you zoom in on a little piece of one, the more it starts to look more
and more like something moving in a straight line at constant speed,
because the deviations from that happen on a larger time scale than
you're seeing.
So this commit adds a rate limit, not on the _collection_ of the data
(we'll still take all the bits we can get, thanks!), but on the rate
at which we increment the _counter_ for how much entropy we think
we've collected so far. That way, the user still has to spend time
wiggling the mouse back and forth in a way that varies with muscle
motions and introduces randomness.
2022-01-08 12:09:36 +00:00
|
|
|
state->entropy_prev_msgtime = GetMessageTime();
|
2019-02-10 13:38:15 +00:00
|
|
|
|
|
|
|
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
|
|
|
|
MAKELPARAM(0, state->entropy_required));
|
|
|
|
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
|
|
|
|
|
2018-06-03 13:41:31 +00:00
|
|
|
smemclr(raw_entropy_buf, raw_entropy_required);
|
|
|
|
sfree(raw_entropy_buf);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_SAVE:
|
2015-05-10 06:42:48 +00:00
|
|
|
case IDC_EXPORT_OPENSSH_AUTO:
|
2015-04-28 18:46:58 +00:00
|
|
|
case IDC_EXPORT_OPENSSH_NEW:
|
2002-05-13 16:56:11 +00:00
|
|
|
case IDC_EXPORT_SSHCOM:
|
2019-09-08 19:29:00 +00:00
|
|
|
if (HIWORD(wParam) != BN_CLICKED)
|
|
|
|
break;
|
|
|
|
state =
|
|
|
|
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (state->key_exists) {
|
|
|
|
char filename[FILENAME_MAX];
|
|
|
|
char *passphrase, *passphrase2;
|
2002-05-13 16:56:11 +00:00
|
|
|
int type, realtype;
|
|
|
|
|
|
|
|
if (state->ssh2)
|
|
|
|
realtype = SSH_KEYTYPE_SSH2;
|
|
|
|
else
|
|
|
|
realtype = SSH_KEYTYPE_SSH1;
|
|
|
|
|
2015-05-10 06:42:48 +00:00
|
|
|
if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
|
|
|
|
type = SSH_KEYTYPE_OPENSSH_AUTO;
|
2015-04-28 18:46:58 +00:00
|
|
|
else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
|
|
|
|
type = SSH_KEYTYPE_OPENSSH_NEW;
|
2002-05-13 16:56:11 +00:00
|
|
|
else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
|
|
|
|
type = SSH_KEYTYPE_SSHCOM;
|
|
|
|
else
|
|
|
|
type = realtype;
|
|
|
|
|
|
|
|
if (type != realtype &&
|
|
|
|
import_target_type(type) != realtype) {
|
|
|
|
char msg[256];
|
2005-03-10 16:36:05 +00:00
|
|
|
sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
|
2002-05-13 16:56:11 +00:00
|
|
|
" format", (state->ssh2 ? 2 : 1),
|
|
|
|
(state->ssh2 ? 1 : 2));
|
2019-09-08 19:29:00 +00:00
|
|
|
MessageBox(hwnd, msg,
|
2002-05-13 16:56:11 +00:00
|
|
|
"PuTTYgen Error", MB_OK | MB_ICONERROR);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
2002-05-13 16:56:11 +00:00
|
|
|
}
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
|
|
|
|
passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
|
|
|
|
if (strcmp(passphrase, passphrase2)) {
|
|
|
|
MessageBox(hwnd,
|
|
|
|
"The two passphrases given do not match.",
|
|
|
|
"PuTTYgen Error", MB_OK | MB_ICONERROR);
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(passphrase);
|
|
|
|
burnstr(passphrase2);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(passphrase2);
|
2019-09-08 19:29:00 +00:00
|
|
|
if (!*passphrase) {
|
|
|
|
int ret;
|
|
|
|
ret = MessageBox(hwnd,
|
|
|
|
"Are you sure you want to save this key\n"
|
|
|
|
"without a passphrase to protect it?",
|
|
|
|
"PuTTYgen Warning",
|
|
|
|
MB_YESNO | MB_ICONWARNING);
|
|
|
|
if (ret != IDYES) {
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(passphrase);
|
|
|
|
break;
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
if (prompt_keyfile(hwnd, "Save private key as:",
|
|
|
|
filename, true, (type == realtype))) {
|
|
|
|
int ret;
|
|
|
|
FILE *fp = fopen(filename, "r");
|
|
|
|
if (fp) {
|
|
|
|
char *buffer;
|
|
|
|
fclose(fp);
|
|
|
|
buffer = dupprintf("Overwrite existing file\n%s?",
|
|
|
|
filename);
|
|
|
|
ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
|
|
|
|
MB_YESNO | MB_ICONWARNING);
|
|
|
|
sfree(buffer);
|
|
|
|
if (ret != IDYES) {
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(passphrase);
|
2019-09-08 19:29:00 +00:00
|
|
|
break;
|
2011-10-02 14:14:21 +00:00
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
2002-05-13 16:56:11 +00:00
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (state->ssh2) {
|
|
|
|
Filename *fn = filename_from_str(filename);
|
2002-05-13 16:56:11 +00:00
|
|
|
if (type != realtype)
|
2011-10-02 11:01:57 +00:00
|
|
|
ret = export_ssh2(fn, type, &state->ssh2key,
|
2002-05-13 16:56:11 +00:00
|
|
|
*passphrase ? passphrase : NULL);
|
|
|
|
else
|
2020-01-05 10:28:45 +00:00
|
|
|
ret = ppk_save_f(fn, &state->ssh2key,
|
Introduce PPK file format version 3.
This removes both uses of SHA-1 in the file format: it was used as the
MAC protecting the key file against tamperproofing, and also used in
the key derivation step that converted the user's passphrase to cipher
and MAC keys.
The MAC is simply upgraded from HMAC-SHA-1 to HMAC-SHA-256; it is
otherwise unchanged in how it's applied (in particular, to what data).
The key derivation is totally reworked, to be based on Argon2, which
I've just added to the code base. This should make stolen encrypted
key files more resistant to brute-force attack.
Argon2 has assorted configurable parameters for memory and CPU usage;
the new key format includes all those parameters. So there's no reason
we can't have them under user control, if a user wants to be
particularly vigorous or particularly lightweight with their own key
files. They could even switch to one of the other flavours of Argon2,
if they thought side channels were an especially large or small risk
in their particular environment. In this commit I haven't added any UI
for controlling that kind of thing, but the PPK loading function is
all set up to cope, so that can all be added in a future commit
without having to change the file format.
While I'm at it, I've also switched the CBC encryption to using a
random IV (or rather, one derived from the passphrase along with the
cipher and MAC keys). That's more like normal SSH-2 practice.
2021-02-20 10:17:45 +00:00
|
|
|
*passphrase ? passphrase : NULL,
|
2021-02-22 20:15:11 +00:00
|
|
|
&save_params);
|
2011-10-02 11:01:57 +00:00
|
|
|
filename_free(fn);
|
2019-09-08 19:29:00 +00:00
|
|
|
} else {
|
|
|
|
Filename *fn = filename_from_str(filename);
|
2002-05-13 16:56:11 +00:00
|
|
|
if (type != realtype)
|
2011-10-02 11:01:57 +00:00
|
|
|
ret = export_ssh1(fn, type, &state->key,
|
2002-05-13 16:56:11 +00:00
|
|
|
*passphrase ? passphrase : NULL);
|
|
|
|
else
|
2020-01-05 10:28:45 +00:00
|
|
|
ret = rsa1_save_f(fn, &state->key,
|
|
|
|
*passphrase ? passphrase : NULL);
|
2011-10-02 11:01:57 +00:00
|
|
|
filename_free(fn);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
if (ret <= 0) {
|
|
|
|
MessageBox(hwnd, "Unable to save key file",
|
|
|
|
"PuTTYgen Error", MB_OK | MB_ICONERROR);
|
|
|
|
}
|
|
|
|
}
|
2011-10-02 14:14:21 +00:00
|
|
|
burnstr(passphrase);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_SAVEPUB:
|
|
|
|
if (HIWORD(wParam) != BN_CLICKED)
|
|
|
|
break;
|
|
|
|
state =
|
|
|
|
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (state->key_exists) {
|
|
|
|
char filename[FILENAME_MAX];
|
|
|
|
if (prompt_keyfile(hwnd, "Save public key as:",
|
|
|
|
filename, true, false)) {
|
|
|
|
int ret;
|
|
|
|
FILE *fp = fopen(filename, "r");
|
|
|
|
if (fp) {
|
|
|
|
char *buffer;
|
|
|
|
fclose(fp);
|
|
|
|
buffer = dupprintf("Overwrite existing file\n%s?",
|
|
|
|
filename);
|
|
|
|
ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
|
|
|
|
MB_YESNO | MB_ICONWARNING);
|
|
|
|
sfree(buffer);
|
|
|
|
if (ret != IDYES)
|
|
|
|
break;
|
|
|
|
}
|
2015-05-12 12:42:26 +00:00
|
|
|
fp = fopen(filename, "w");
|
|
|
|
if (!fp) {
|
|
|
|
MessageBox(hwnd, "Unable to open key file",
|
|
|
|
"PuTTYgen Error", MB_OK | MB_ICONERROR);
|
|
|
|
} else {
|
|
|
|
if (state->ssh2) {
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *blob = strbuf_new();
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_public_blob(
|
|
|
|
state->ssh2key.key, BinarySink_UPCAST(blob));
|
2015-05-12 12:42:26 +00:00
|
|
|
ssh2_write_pubkey(fp, state->ssh2key.comment,
|
2018-05-24 09:59:39 +00:00
|
|
|
blob->u, blob->len,
|
2015-05-12 12:42:26 +00:00
|
|
|
SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(blob);
|
2015-05-12 12:42:26 +00:00
|
|
|
} else {
|
|
|
|
ssh1_write_pubkey(fp, &state->key);
|
|
|
|
}
|
|
|
|
if (fclose(fp) < 0) {
|
|
|
|
MessageBox(hwnd, "Unable to save key file",
|
|
|
|
"PuTTYgen Error", MB_OK | MB_ICONERROR);
|
|
|
|
}
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_LOAD:
|
|
|
|
case IDC_IMPORT:
|
|
|
|
if (HIWORD(wParam) != BN_CLICKED)
|
|
|
|
break;
|
|
|
|
state =
|
|
|
|
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (!state->generation_thread_exists) {
|
|
|
|
char filename[FILENAME_MAX];
|
|
|
|
if (prompt_keyfile(hwnd, "Load private key:", filename, false,
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
LOWORD(wParam) == IDC_LOAD)) {
|
2013-07-22 07:11:54 +00:00
|
|
|
Filename *fn = filename_from_str(filename);
|
2019-09-08 19:29:00 +00:00
|
|
|
load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
|
2013-07-22 07:11:54 +00:00
|
|
|
filename_free(fn);
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
break;
|
2022-04-19 16:59:46 +00:00
|
|
|
case IDC_ADDCERT:
|
|
|
|
if (HIWORD(wParam) != BN_CLICKED)
|
|
|
|
break;
|
|
|
|
state =
|
|
|
|
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (state->key_exists && !state->generation_thread_exists) {
|
|
|
|
char filename[FILENAME_MAX];
|
|
|
|
if (prompt_keyfile(hwnd, "Load certificate:", filename, false,
|
|
|
|
false)) {
|
|
|
|
Filename *fn = filename_from_str(filename);
|
|
|
|
add_certificate(hwnd, state, fn);
|
|
|
|
filename_free(fn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IDC_REMCERT:
|
|
|
|
if (HIWORD(wParam) != BN_CLICKED)
|
|
|
|
break;
|
|
|
|
state =
|
|
|
|
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (state->key_exists && !state->generation_thread_exists) {
|
|
|
|
remove_certificate(hwnd, state);
|
|
|
|
}
|
|
|
|
break;
|
2022-07-30 13:41:08 +00:00
|
|
|
case IDC_CERTMOREINFO: {
|
|
|
|
if (HIWORD(wParam) != BN_CLICKED)
|
|
|
|
break;
|
|
|
|
state =
|
|
|
|
(struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (!state->key_exists || !state->ssh2 || !state->ssh2key.key)
|
|
|
|
break;
|
|
|
|
if (!ssh_key_alg(state->ssh2key.key)->is_certificate)
|
|
|
|
break;
|
|
|
|
|
|
|
|
struct certinfo_dialog_ctx ctx[1];
|
|
|
|
ctx->text = ssh_key_cert_info(state->ssh2key.key);
|
|
|
|
ShinyDialogBox(hinst, MAKEINTRESOURCE(216),
|
|
|
|
"PuTTYgenCertInfo", hwnd, CertInfoProc, ctx);
|
|
|
|
seat_dialog_text_free(ctx->text);
|
|
|
|
break;
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_DONEKEY:
|
2019-09-08 19:29:00 +00:00
|
|
|
state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
state->generation_thread_exists = false;
|
|
|
|
state->key_exists = true;
|
|
|
|
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
|
|
|
|
MAKELPARAM(0, PROGRESSRANGE));
|
|
|
|
SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
|
|
|
|
if (state->ssh2) {
|
2014-11-01 09:14:19 +00:00
|
|
|
if (state->keytype == DSA) {
|
2021-04-22 17:28:35 +00:00
|
|
|
state->ssh2key.key = &state->dsakey.sshk;
|
2014-11-01 09:45:20 +00:00
|
|
|
} else if (state->keytype == ECDSA) {
|
2018-06-03 11:58:05 +00:00
|
|
|
state->ssh2key.key = &state->eckey.sshk;
|
2020-03-02 07:05:19 +00:00
|
|
|
} else if (state->keytype == EDDSA) {
|
Complete rewrite of PuTTY's bignum library.
The old 'Bignum' data type is gone completely, and so is sshbn.c. In
its place is a new thing called 'mp_int', handled by an entirely new
library module mpint.c, with API differences both large and small.
The main aim of this change is that the new library should be free of
timing- and cache-related side channels. I've written the code so that
it _should_ - assuming I haven't made any mistakes - do all of its
work without either control flow or memory addressing depending on the
data words of the input numbers. (Though, being an _arbitrary_
precision library, it does have to at least depend on the sizes of the
numbers - but there's a 'formal' size that can vary separately from
the actual magnitude of the represented integer, so if you want to
keep it secret that your number is actually small, it should work fine
to have a very long mp_int and just happen to store 23 in it.) So I've
done all my conditionalisation by means of computing both answers and
doing bit-masking to swap the right one into place, and all loops over
the words of an mp_int go up to the formal size rather than the actual
size.
I haven't actually tested the constant-time property in any rigorous
way yet (I'm still considering the best way to do it). But this code
is surely at the very least a big improvement on the old version, even
if I later find a few more things to fix.
I've also completely rewritten the low-level elliptic curve arithmetic
from sshecc.c; the new ecc.c is closer to being an adjunct of mpint.c
than it is to the SSH end of the code. The new elliptic curve code
keeps all coordinates in Montgomery-multiplication transformed form to
speed up all the multiplications mod the same prime, and only converts
them back when you ask for the affine coordinates. Also, I adopted
extended coordinates for the Edwards curve implementation.
sshecc.c has also had a near-total rewrite in the course of switching
it over to the new system. While I was there, I've separated ECDSA and
EdDSA more completely - they now have separate vtables, instead of a
single vtable in which nearly every function had a big if statement in
it - and also made the externally exposed types for an ECDSA key and
an ECDH context different.
A minor new feature: since the new arithmetic code includes a modular
square root function, we can now support the compressed point
representation for the NIST curves. We seem to have been getting along
fine without that so far, but it seemed a shame not to put it in,
since it was suddenly easy.
In sshrsa.c, one major change is that I've removed the RSA blinding
step in rsa_privkey_op, in which we randomise the ciphertext before
doing the decryption. The purpose of that was to avoid timing leaks
giving away the plaintext - but the new arithmetic code should take
that in its stride in the course of also being careful enough to avoid
leaking the _private key_, which RSA blinding had no way to do
anything about in any case.
Apart from those specific points, most of the rest of the changes are
more or less mechanical, just changing type names and translating code
into the new API.
2018-12-31 13:53:41 +00:00
|
|
|
state->ssh2key.key = &state->edkey.sshk;
|
2019-09-08 19:29:00 +00:00
|
|
|
} else {
|
|
|
|
state->ssh2key.key = &state->key.sshk;
|
|
|
|
}
|
|
|
|
state->commentptr = &state->ssh2key.comment;
|
|
|
|
} else {
|
|
|
|
state->commentptr = &state->key.comment;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Invent a comment for the key. We'll do this by including
|
|
|
|
* the date in it. This will be so horrifyingly ugly that
|
|
|
|
* the user will immediately want to change it, which is
|
|
|
|
* what we want :-)
|
|
|
|
*/
|
|
|
|
*state->commentptr = snewn(30, char);
|
|
|
|
{
|
|
|
|
struct tm tm;
|
|
|
|
tm = ltime();
|
2014-11-01 09:14:19 +00:00
|
|
|
if (state->keytype == DSA)
|
2019-09-08 19:29:00 +00:00
|
|
|
strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
|
2014-11-01 09:45:20 +00:00
|
|
|
else if (state->keytype == ECDSA)
|
|
|
|
strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
|
2020-03-02 07:05:19 +00:00
|
|
|
else if (state->keytype == EDDSA)
|
|
|
|
strftime(*state->commentptr, 30, "eddsa-key-%Y%m%d", &tm);
|
2019-09-08 19:29:00 +00:00
|
|
|
else
|
|
|
|
strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now update the key controls with all the key data.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
char *fp, *savecomment;
|
|
|
|
/*
|
|
|
|
* Blank passphrase, initially. This isn't dangerous,
|
|
|
|
* because we will warn (Are You Sure?) before allowing
|
|
|
|
* the user to save an unprotected private key.
|
|
|
|
*/
|
|
|
|
SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
|
|
|
|
SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
|
|
|
|
/*
|
|
|
|
* Set the comment.
|
|
|
|
*/
|
|
|
|
SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
|
|
|
|
/*
|
|
|
|
* Set the key fingerprint.
|
|
|
|
*/
|
|
|
|
savecomment = *state->commentptr;
|
|
|
|
*state->commentptr = NULL;
|
|
|
|
if (state->ssh2)
|
2021-03-13 09:58:14 +00:00
|
|
|
fp = ssh2_fingerprint(state->ssh2key.key, state->fptype);
|
2018-06-03 07:08:53 +00:00
|
|
|
else
|
|
|
|
fp = rsa_ssh1_fingerprint(&state->key);
|
|
|
|
SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
|
|
|
|
sfree(fp);
|
2019-09-08 19:29:00 +00:00
|
|
|
*state->commentptr = savecomment;
|
|
|
|
/*
|
|
|
|
* Construct a decimal representation of the key, for
|
|
|
|
* pasting into .ssh/authorized_keys or
|
|
|
|
* .ssh/authorized_keys2 on a Unix box.
|
|
|
|
*/
|
|
|
|
if (state->ssh2) {
|
2022-07-30 13:41:08 +00:00
|
|
|
setupbigedit2(hwnd, &state->ssh2key);
|
2019-09-08 19:29:00 +00:00
|
|
|
} else {
|
2022-07-30 13:41:08 +00:00
|
|
|
setupbigedit1(hwnd, &state->key);
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Finally, hide the progress bar and show the key data.
|
|
|
|
*/
|
|
|
|
ui_set_state(hwnd, state, 2);
|
|
|
|
break;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
case WM_HELP: {
|
|
|
|
int id = ((LPHELPINFO)lParam)->iCtrlId;
|
|
|
|
const char *topic = NULL;
|
|
|
|
switch (id) {
|
|
|
|
case IDC_GENERATING:
|
|
|
|
case IDC_PROGRESS:
|
|
|
|
case IDC_GENSTATIC:
|
|
|
|
case IDC_GENERATE:
|
|
|
|
topic = WINHELP_CTX_puttygen_generate; break;
|
|
|
|
case IDC_PKSTATIC:
|
|
|
|
case IDC_KEYDISPLAY:
|
|
|
|
topic = WINHELP_CTX_puttygen_pastekey; break;
|
|
|
|
case IDC_FPSTATIC:
|
|
|
|
case IDC_FINGERPRINT:
|
|
|
|
topic = WINHELP_CTX_puttygen_fingerprint; break;
|
|
|
|
case IDC_COMMENTSTATIC:
|
|
|
|
case IDC_COMMENTEDIT:
|
|
|
|
topic = WINHELP_CTX_puttygen_comment; break;
|
|
|
|
case IDC_PASSPHRASE1STATIC:
|
|
|
|
case IDC_PASSPHRASE1EDIT:
|
|
|
|
case IDC_PASSPHRASE2STATIC:
|
|
|
|
case IDC_PASSPHRASE2EDIT:
|
|
|
|
topic = WINHELP_CTX_puttygen_passphrase; break;
|
|
|
|
case IDC_LOADSTATIC:
|
|
|
|
case IDC_LOAD:
|
|
|
|
topic = WINHELP_CTX_puttygen_load; break;
|
|
|
|
case IDC_SAVESTATIC:
|
|
|
|
case IDC_SAVE:
|
|
|
|
topic = WINHELP_CTX_puttygen_savepriv; break;
|
|
|
|
case IDC_SAVEPUB:
|
|
|
|
topic = WINHELP_CTX_puttygen_savepub; break;
|
|
|
|
case IDC_TYPESTATIC:
|
|
|
|
case IDC_KEYSSH1:
|
|
|
|
case IDC_KEYSSH2RSA:
|
|
|
|
case IDC_KEYSSH2DSA:
|
|
|
|
case IDC_KEYSSH2ECDSA:
|
2020-03-02 07:05:19 +00:00
|
|
|
case IDC_KEYSSH2EDDSA:
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
topic = WINHELP_CTX_puttygen_keytype; break;
|
|
|
|
case IDC_BITSSTATIC:
|
|
|
|
case IDC_BITS:
|
|
|
|
topic = WINHELP_CTX_puttygen_bits; break;
|
|
|
|
case IDC_IMPORT:
|
|
|
|
case IDC_EXPORT_OPENSSH_AUTO:
|
|
|
|
case IDC_EXPORT_OPENSSH_NEW:
|
|
|
|
case IDC_EXPORT_SSHCOM:
|
|
|
|
topic = WINHELP_CTX_puttygen_conversions; break;
|
|
|
|
}
|
|
|
|
if (topic) {
|
2022-08-03 19:48:46 +00:00
|
|
|
launch_help(hwnd, topic);
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
} else {
|
2022-08-03 19:48:46 +00:00
|
|
|
MessageBeep(0);
|
2001-12-12 18:45:56 +00:00
|
|
|
}
|
|
|
|
break;
|
Formatting change to braces around one case of a switch.
Sometimes, within a switch statement, you want to declare local
variables specific to the handler for one particular case. Until now
I've mostly been writing this in the form
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED:
{
declare variables;
do stuff;
}
break;
}
which is ugly because the two pieces of essentially similar code
appear at different indent levels, and also inconvenient because you
have less horizontal space available to write the complicated case
handler in - particuarly undesirable because _complicated_ case
handlers are the ones most likely to need all the space they can get!
After encountering a rather nicer idiom in the LLVM source code, and
after a bit of hackery this morning figuring out how to persuade
Emacs's auto-indent to do what I wanted with it, I've decided to move
to an idiom in which the open brace comes right after the case
statement, and the code within it is indented the same as it would
have been without the brace. Then the whole case handler (including
the break) lives inside those braces, and you get something that looks
more like this:
switch (discriminant) {
case SIMPLE:
do stuff;
break;
case COMPLICATED: {
declare variables;
do stuff;
break;
}
}
This commit is a big-bang change that reformats all the complicated
case handlers I could find into the new layout. This is particularly
nice in the Pageant main function, in which almost _every_ case
handler had a bundle of variables and was long and complicated. (In
fact that's what motivated me to get round to this.) Some of the
innermost parts of the terminal escape-sequence handling are also
breathing a bit easier now the horizontal pressure on them is
relieved.
(Also, in a few cases, I was able to remove the extra braces
completely, because the only variable local to the case handler was a
loop variable which our new C99 policy allows me to move into the
initialiser clause of its for statement.)
Viewed with whitespace ignored, this is not too disruptive a change.
Downstream patches that conflict with it may need to be reapplied
using --ignore-whitespace or similar.
2020-02-16 07:49:52 +00:00
|
|
|
}
|
2000-10-19 15:43:08 +00:00
|
|
|
case WM_CLOSE:
|
2019-09-08 19:29:00 +00:00
|
|
|
state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
sfree(state);
|
|
|
|
quit_help(hwnd);
|
|
|
|
EndDialog(hwnd, 1);
|
|
|
|
return 0;
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-12-17 11:16:07 +00:00
|
|
|
void cleanup_exit(int code)
|
|
|
|
{
|
|
|
|
shutdown_help();
|
|
|
|
exit(code);
|
|
|
|
}
|
2002-03-06 20:13:22 +00:00
|
|
|
|
Remove remaining uses of the GLOBAL macro.
We now have no remaining things in header files that switch from being
a declaration to a definition depending on an awkward #define at the
point of including that header. There are still a few mutable
variables with external linkage, but at least now each one is defined
in a specific source file file appropriate to its purpose and context.
The remaining globals as of this commit were:
- 'logctx' and 'term', which never needed to be globals in the first
place, because they were never actually shared between source
files. Now 'term' is just a static in window.c, and 'logctx' is a
static in each of that and winplink.c.
- 'hinst', which still has external linkage, but is now defined
separately in each source file that sets it up (i.e. those with a
WinMain)
- osMajorVersion, osMinorVersion and osPlatformId, whose definitions
now live in winmisc.c alongside the code which sets them up.
(Actually they were defined there all along, it turns out, but
every toolchain I've built with has commoned them together with the
version defined by the GLOBAL in the header.)
- 'hwnd', which nothing was actually _using_ any more after previous
commits, so all this commit had to do was delete it.
2020-02-02 10:00:43 +00:00
|
|
|
HINSTANCE hinst;
|
|
|
|
|
2022-01-15 18:27:19 +00:00
|
|
|
static NORETURN void opt_error(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
char *msg = dupvprintf(fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
MessageBox(NULL, msg, "PuTTYgen command line error", MB_ICONERROR | MB_OK);
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2001-05-06 14:35:20 +00:00
|
|
|
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
|
|
|
{
|
2006-12-17 11:16:07 +00:00
|
|
|
int ret;
|
2022-01-15 18:30:34 +00:00
|
|
|
struct InitialParams params[1];
|
2002-08-06 17:48:14 +00:00
|
|
|
|
2016-07-18 19:02:32 +00:00
|
|
|
dll_hijacking_protection();
|
|
|
|
|
2017-03-13 21:42:44 +00:00
|
|
|
init_common_controls();
|
2000-10-19 15:43:08 +00:00
|
|
|
hinst = inst;
|
2001-12-12 18:45:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* See if we can find our Help file.
|
|
|
|
*/
|
2006-12-17 11:16:07 +00:00
|
|
|
init_help();
|
2001-12-12 18:45:56 +00:00
|
|
|
|
2022-01-15 18:30:34 +00:00
|
|
|
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;
|
|
|
|
|
2022-01-15 18:27:19 +00:00
|
|
|
int argbits = -1;
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
AuxMatchOpt amo = aux_match_opt_init(opt_error);
|
2022-01-15 18:27:19 +00:00
|
|
|
while (!aux_match_done(&amo)) {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
CmdlineArg *valarg;
|
2022-01-15 18:27:19 +00:00
|
|
|
#define match_opt(...) aux_match_opt( \
|
|
|
|
&amo, NULL, __VA_ARGS__, (const char *)NULL)
|
|
|
|
#define match_optval(...) aux_match_opt( \
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
&amo, &valarg, __VA_ARGS__, (const char *)NULL)
|
2022-01-15 18:27:19 +00:00
|
|
|
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
if (aux_match_arg(&amo, &valarg)) {
|
2022-01-15 18:27:19 +00:00
|
|
|
if (!cmdline_keyfile) {
|
|
|
|
/*
|
|
|
|
* Assume the first argument to be a private key file, and
|
|
|
|
* attempt to load it.
|
|
|
|
*/
|
2024-09-24 16:50:19 +00:00
|
|
|
cmdline_keyfile = cmdline_arg_to_filename(valarg);
|
2022-01-15 18:27:19 +00:00
|
|
|
continue;
|
|
|
|
} else {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
opt_error("unexpected extra argument '%s'\n",
|
|
|
|
cmdline_arg_to_str(valarg));
|
2022-01-15 18:27:19 +00:00
|
|
|
}
|
|
|
|
} else if (match_opt("-pgpfp")) {
|
2020-02-02 10:00:42 +00:00
|
|
|
pgp_fingerprints_msgbox(NULL);
|
2024-09-25 15:26:17 +00:00
|
|
|
return 0;
|
2022-01-15 18:27:19 +00:00
|
|
|
} else if (match_opt("-restrict-acl", "-restrict_acl",
|
|
|
|
"-restrictacl")) {
|
2017-01-28 21:56:28 +00:00
|
|
|
restrict_process_acl();
|
2022-01-15 18:30:34 +00:00
|
|
|
} else if (match_optval("-t")) {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
const char *val = cmdline_arg_to_str(valarg);
|
2022-01-15 18:30:34 +00:00
|
|
|
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")) {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
argbits = atoi(cmdline_arg_to_str(valarg));
|
2022-01-15 18:30:34 +00:00
|
|
|
} else if (match_optval("-E")) {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
const char *val = cmdline_arg_to_str(valarg);
|
2022-01-15 18:30:34 +00:00
|
|
|
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")) {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
const char *val = cmdline_arg_to_str(valarg);
|
2022-01-15 18:30:34 +00:00
|
|
|
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")) {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
char *val = dupstr(cmdline_arg_to_str(valarg));
|
2022-01-15 18:30:34 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
sfree(val);
|
2022-04-02 15:18:08 +00:00
|
|
|
} else if (match_optval("-demo-screenshot")) {
|
2024-09-24 16:50:19 +00:00
|
|
|
demo_screenshot_filename = cmdline_arg_to_filename(valarg);
|
2022-04-02 15:18:08 +00:00
|
|
|
cmdline_demo_keystr = PTRLEN_LITERAL(
|
|
|
|
"PuTTY-User-Key-File-3: ssh-ed25519\n"
|
|
|
|
"Encryption: none\n"
|
|
|
|
"Comment: ed25519-key-20220402\n"
|
|
|
|
"Public-Lines: 2\n"
|
|
|
|
"AAAAC3NzaC1lZDI1NTE5AAAAILzuIFwZ"
|
|
|
|
"8ZhgOlilcSb+9zPuCf/DmKJiloVlmWGy\n"
|
|
|
|
"xa/F\n"
|
|
|
|
"Private-Lines: 1\n"
|
|
|
|
"AAAAIPca6vLwtB2NJhZUpABQISR0gcQH8jjQLta19VyzA3wc\n"
|
|
|
|
"Private-MAC: 1159e9628259b35933b397379bbe8a14"
|
|
|
|
"a1f1d97fe91e446e45a9581a3408b70e\n");
|
|
|
|
params->keybutton = IDC_KEYSSH2EDDSA;
|
|
|
|
argbits = 255;
|
2019-09-08 19:29:00 +00:00
|
|
|
} else {
|
New abstraction for command-line arguments.
This begins the process of enabling our Windows applications to handle
Unicode characters on their command lines which don't fit in the
system code page.
Instead of passing plain strings to cmdline_process_param, we now pass
a partially opaque and platform-specific thing called a CmdlineArg.
This has a method that extracts the argument word as a default-encoded
string, and another one that tries to extract it as UTF-8 (though it
may fail if the UTF-8 isn't available).
On Windows, the command line is now constructed by calling
split_into_argv_w on the Unicode command line returned by
GetCommandLineW(), and the UTF-8 method returns text converted
directly from that wide-character form, not going via the system code
page. So it _can_ include UTF-8 characters that wouldn't have
round-tripped via CP_ACP.
This commit introduces the abstraction and switches over the
cross-platform and Windows argv-handling code to use it, with minimal
functional change. Nothing yet tries to call cmdline_arg_get_utf8().
I say 'cross-platform and Windows' because on the Unix side there's
still a lot of use of plain old argv which I haven't converted. That
would be a much larger project, and isn't currently needed: the
_current_ aim of this abstraction is to get the right things to happen
relating to Unicode on Windows, so for code that doesn't run on
Windows anyway, it's not adding value. (Also there's a tension with
GTK, which wants to talk to standard argv and extract arguments _it_
knows about, so at the very least we'd have to let it munge argv
before importing it into this new system.)
2024-09-25 09:18:38 +00:00
|
|
|
opt_error("unrecognised option '%s'\n",
|
|
|
|
cmdline_arg_to_str(amo.arglist->args[amo.index]));
|
2019-09-08 19:29:00 +00:00
|
|
|
}
|
2005-03-19 02:26:58 +00:00
|
|
|
}
|
|
|
|
|
2022-01-15 18:30:34 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-22 20:15:11 +00:00
|
|
|
|
Replace PuTTY's PRNG with a Fortuna-like system.
This tears out the entire previous random-pool system in sshrand.c. In
its place is a system pretty close to Ferguson and Schneier's
'Fortuna' generator, with the main difference being that I use SHA-256
instead of AES for the generation side of the system (rationale given
in comment).
The PRNG implementation lives in sshprng.c, and defines a self-
contained data type with no state stored outside the object, so you
can instantiate however many of them you like. The old sshrand.c still
exists, but in place of the previous random pool system, it's just
become a client of sshprng.c, whose job is to hold a single global
instance of the PRNG type, and manage its reference count, save file,
noise-collection timers and similar administrative business.
Advantages of this change include:
- Fortuna is designed with a more varied threat model in mind than my
old home-grown random pool. For example, after any request for
random numbers, it automatically re-seeds itself, so that if the
state of the PRNG should be leaked, it won't give enough
information to find out what past outputs _were_.
- The PRNG type can be instantiated with any hash function; the
instance used by the main tools is based on SHA-256, an improvement
on the old pool's use of SHA-1.
- The new PRNG only uses the completely standard interface to the
hash function API, instead of having to have privileged access to
the internal SHA-1 block transform function. This will make it
easier to revamp the hash code in general, and also it means that
hardware-accelerated versions of SHA-256 will automatically be used
for the PRNG as well as for everything else.
- The new PRNG can be _tested_! Because it has an actual (if not
quite explicit) specification for exactly what the output numbers
_ought_ to be derived from the hashes of, I can (and have) put
tests in cryptsuite that ensure the output really is being derived
in the way I think it is. The old pool could have been returning
any old nonsense and it would have been very hard to tell for sure.
2019-01-22 22:42:41 +00:00
|
|
|
random_setup_special();
|
2022-01-15 18:30:34 +00:00
|
|
|
ret = DialogBoxParam(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc,
|
|
|
|
(LPARAM)params) != IDOK;
|
2006-12-17 11:16:07 +00:00
|
|
|
|
|
|
|
cleanup_exit(ret);
|
2019-09-08 19:29:00 +00:00
|
|
|
return ret; /* just in case optimiser complains */
|
2000-10-19 15:43:08 +00:00
|
|
|
}
|