2004-01-22 19:15:32 +00:00
|
|
|
/*
|
|
|
|
* cmdgen.c - command-line form of PuTTYgen
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define PUTTY_DO_GLOBALS
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <time.h>
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
2004-01-22 19:15:32 +00:00
|
|
|
|
|
|
|
#include "putty.h"
|
|
|
|
#include "ssh.h"
|
|
|
|
|
2004-01-24 17:16:37 +00:00
|
|
|
#ifdef TEST_CMDGEN
|
|
|
|
/*
|
|
|
|
* This section overrides some definitions below for test purposes.
|
2016-03-30 07:25:25 +00:00
|
|
|
* When compiled with -DTEST_CMDGEN (as cgtest.c will do):
|
2004-01-24 17:16:37 +00:00
|
|
|
*
|
|
|
|
* - Calls to get_random_data() are replaced with the diagnostic
|
|
|
|
* function below (I #define the name so that I can still link
|
|
|
|
* with the original set of modules without symbol clash), in
|
|
|
|
* order to avoid depleting the test system's /dev/random
|
|
|
|
* unnecessarily.
|
|
|
|
*
|
2005-10-30 20:24:09 +00:00
|
|
|
* - Calls to console_get_userpass_input() are replaced with the
|
|
|
|
* diagnostic function below, so that I can run tests in an
|
|
|
|
* automated manner and provide their interactive passphrase
|
|
|
|
* inputs.
|
2004-01-24 17:16:37 +00:00
|
|
|
*
|
|
|
|
* - main() is renamed to cmdgen_main(); at the bottom of the file
|
|
|
|
* I define another main() which calls the former repeatedly to
|
|
|
|
* run tests.
|
|
|
|
*/
|
2004-01-22 19:15:32 +00:00
|
|
|
#define get_random_data get_random_data_diagnostic
|
2016-03-30 07:17:03 +00:00
|
|
|
char *get_random_data(int len, const char *device)
|
2004-01-22 19:15:32 +00:00
|
|
|
{
|
|
|
|
char *buf = snewn(len, char);
|
|
|
|
memset(buf, 'x', len);
|
|
|
|
return buf;
|
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
#define console_get_userpass_input console_get_userpass_input_diagnostic
|
2004-01-24 17:16:37 +00:00
|
|
|
int nprompts, promptsgot;
|
|
|
|
const char *prompts[3];
|
2018-05-18 06:22:56 +00:00
|
|
|
int console_get_userpass_input(prompts_t *p)
|
2004-01-24 17:16:37 +00:00
|
|
|
{
|
2005-10-30 20:24:09 +00:00
|
|
|
size_t i;
|
|
|
|
int ret = 1;
|
|
|
|
for (i = 0; i < p->n_prompts; i++) {
|
|
|
|
if (promptsgot < nprompts) {
|
2016-03-30 07:25:25 +00:00
|
|
|
p->prompts[i]->result = dupstr(prompts[promptsgot++]);
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
|
|
|
promptsgot++; /* track number of requests anyway */
|
|
|
|
ret = 0;
|
|
|
|
}
|
2004-01-24 17:16:37 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
return ret;
|
2004-01-24 17:16:37 +00:00
|
|
|
}
|
|
|
|
#define main cmdgen_main
|
2004-01-22 19:15:32 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
struct progress {
|
|
|
|
int phase, current;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void progress_update(void *param, int action, int phase, int iprogress)
|
|
|
|
{
|
|
|
|
struct progress *p = (struct progress *)param;
|
|
|
|
if (action != PROGFN_PROGRESS)
|
|
|
|
return;
|
|
|
|
if (phase > p->phase) {
|
|
|
|
if (p->phase >= 0)
|
|
|
|
fputc('\n', stderr);
|
|
|
|
p->phase = phase;
|
|
|
|
if (iprogress >= 0)
|
|
|
|
p->current = iprogress - 1;
|
|
|
|
else
|
|
|
|
p->current = iprogress;
|
|
|
|
}
|
|
|
|
while (p->current < iprogress) {
|
|
|
|
fputc('+', stdout);
|
|
|
|
p->current++;
|
|
|
|
}
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void no_progress(void *param, int action, int phase, int iprogress)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-05-15 10:15:42 +00:00
|
|
|
void modalfatalbox(const char *p, ...)
|
2004-01-22 19:15:32 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "FATAL ERROR: ");
|
|
|
|
va_start(ap, p);
|
|
|
|
vfprintf(stderr, p, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
cleanup_exit(1);
|
|
|
|
}
|
|
|
|
|
2015-05-15 10:15:42 +00:00
|
|
|
void nonfatal(const char *p, ...)
|
2013-07-19 17:44:28 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "ERROR: ");
|
|
|
|
va_start(ap, p);
|
|
|
|
vfprintf(stderr, p, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
}
|
|
|
|
|
2004-01-22 19:15:32 +00:00
|
|
|
/*
|
|
|
|
* Stubs to let everything else link sensibly.
|
|
|
|
*/
|
|
|
|
void log_eventlog(void *handle, const char *event)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
char *x_get_default(const char *key)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
void sk_cleanup(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void showversion(void)
|
|
|
|
{
|
2017-01-21 14:55:53 +00:00
|
|
|
char *buildinfo_text = buildinfo("\n");
|
2017-02-11 20:12:55 +00:00
|
|
|
printf("puttygen: %s\n%s\n", ver, buildinfo_text);
|
2017-01-21 14:55:53 +00:00
|
|
|
sfree(buildinfo_text);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
2006-06-17 13:01:04 +00:00
|
|
|
void usage(int standalone)
|
2004-01-22 19:15:32 +00:00
|
|
|
{
|
2017-02-11 20:12:55 +00:00
|
|
|
fprintf(standalone ? stderr : stdout,
|
2004-01-22 19:15:32 +00:00
|
|
|
"Usage: puttygen ( keyfile | -t type [ -b bits ] )\n"
|
2006-07-07 14:18:47 +00:00
|
|
|
" [ -C comment ] [ -P ] [ -q ]\n"
|
2004-01-23 13:21:17 +00:00
|
|
|
" [ -o output-keyfile ] [ -O type | -l | -L"
|
|
|
|
" | -p ]\n");
|
2006-06-17 13:01:04 +00:00
|
|
|
if (standalone)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Use \"puttygen --help\" for more detail.\n");
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void help(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Help message is an extended version of the usage message. So
|
|
|
|
* start with that, plus a version heading.
|
|
|
|
*/
|
2017-02-11 20:12:55 +00:00
|
|
|
printf("PuTTYgen: key generator and converter for the PuTTY tools\n"
|
|
|
|
"%s\n", ver);
|
2006-06-17 13:01:04 +00:00
|
|
|
usage(FALSE);
|
2017-02-11 20:12:55 +00:00
|
|
|
printf(" -t specify key type when generating (ed25519, ecdsa, rsa, "
|
2015-05-30 11:11:32 +00:00
|
|
|
"dsa, rsa1)\n"
|
2017-02-11 20:12:55 +00:00
|
|
|
" -b specify number of bits when generating key\n"
|
|
|
|
" -C change or specify key comment\n"
|
|
|
|
" -P change key passphrase\n"
|
|
|
|
" -q quiet: do not display progress bar\n"
|
|
|
|
" -O specify output type:\n"
|
|
|
|
" private output PuTTY private key format\n"
|
|
|
|
" private-openssh export OpenSSH private key\n"
|
|
|
|
" private-openssh-new export OpenSSH private key "
|
|
|
|
"(force new format)\n"
|
|
|
|
" private-sshcom export ssh.com private key\n"
|
|
|
|
" public RFC 4716 / ssh.com public key\n"
|
|
|
|
" public-openssh OpenSSH public key\n"
|
|
|
|
" fingerprint output the key fingerprint\n"
|
|
|
|
" -o specify output file\n"
|
|
|
|
" -l equivalent to `-O fingerprint'\n"
|
|
|
|
" -L equivalent to `-O public-openssh'\n"
|
|
|
|
" -p equivalent to `-O public'\n"
|
|
|
|
" --old-passphrase file\n"
|
|
|
|
" specify file containing old key passphrase\n"
|
|
|
|
" --new-passphrase file\n"
|
|
|
|
" specify file containing new key passphrase\n"
|
|
|
|
" --random-device device\n"
|
|
|
|
" specify device to read entropy from (e.g. /dev/urandom)\n"
|
|
|
|
);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
2004-01-24 17:16:37 +00:00
|
|
|
static int move(char *from, char *to)
|
2004-01-22 19:15:32 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = rename(from, to);
|
|
|
|
if (ret) {
|
|
|
|
/*
|
|
|
|
* This OS may require us to remove the original file first.
|
|
|
|
*/
|
|
|
|
remove(to);
|
|
|
|
ret = rename(from, to);
|
|
|
|
}
|
|
|
|
if (ret) {
|
|
|
|
perror("puttygen: cannot move new file on to old one");
|
2004-01-24 17:16:37 +00:00
|
|
|
return FALSE;
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
2004-01-24 17:16:37 +00:00
|
|
|
return TRUE;
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
static char *readpassphrase(const char *filename)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
char *line;
|
|
|
|
|
|
|
|
fp = fopen(filename, "r");
|
|
|
|
if (!fp) {
|
|
|
|
fprintf(stderr, "puttygen: cannot open %s: %s\n",
|
|
|
|
filename, strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
line = fgetline(fp);
|
|
|
|
if (line)
|
|
|
|
line[strcspn(line, "\r\n")] = '\0';
|
|
|
|
else if (ferror(fp))
|
|
|
|
fprintf(stderr, "puttygen: error reading from %s: %s\n",
|
|
|
|
filename, strerror(errno));
|
|
|
|
else /* empty file */
|
|
|
|
line = dupstr("");
|
|
|
|
fclose(fp);
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
2016-04-02 07:00:37 +00:00
|
|
|
#define DEFAULT_RSADSA_BITS 2048
|
|
|
|
|
2017-02-22 22:10:05 +00:00
|
|
|
/* For Unix in particular, but harmless if this main() is reused elsewhere */
|
|
|
|
const int buildinfo_gtk_relevant = FALSE;
|
|
|
|
|
2004-01-22 19:15:32 +00:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *infile = NULL;
|
2012-03-05 18:32:27 +00:00
|
|
|
Filename *infilename = NULL, *outfilename = NULL;
|
2015-05-09 14:02:54 +00:00
|
|
|
enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, ED25519 } keytype = NOKEYGEN;
|
2004-01-22 19:15:32 +00:00
|
|
|
char *outfile = NULL, *outfiletmp = NULL;
|
2015-05-10 06:42:48 +00:00
|
|
|
enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO,
|
2015-04-28 18:46:58 +00:00
|
|
|
OPENSSH_NEW, SSHCOM } outtype = PRIVATE;
|
2015-05-09 14:02:47 +00:00
|
|
|
int bits = -1;
|
2004-01-22 19:15:32 +00:00
|
|
|
char *comment = NULL, *origcomment = NULL;
|
|
|
|
int change_passphrase = FALSE;
|
|
|
|
int errs = FALSE, nogo = FALSE;
|
|
|
|
int intype = SSH_KEYTYPE_UNOPENABLE;
|
|
|
|
int sshver = 0;
|
|
|
|
struct ssh2_userkey *ssh2key = NULL;
|
|
|
|
struct RSAKey *ssh1key = NULL;
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *ssh2blob = NULL;
|
2006-12-30 23:00:14 +00:00
|
|
|
char *ssh2alg = NULL;
|
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 *ssh2algf = NULL;
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
char *old_passphrase = NULL, *new_passphrase = NULL;
|
2004-01-22 19:15:32 +00:00
|
|
|
int load_encrypted;
|
|
|
|
progfn_t progressfn = is_interactive() ? progress_update : no_progress;
|
2016-03-30 07:17:03 +00:00
|
|
|
const char *random_device = NULL;
|
2004-01-22 19:15:32 +00:00
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
* Parse the command line to figure out what we've been asked to do.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If run with no arguments at all, print the usage message and
|
|
|
|
* return success.
|
|
|
|
*/
|
|
|
|
if (argc <= 1) {
|
2006-06-17 13:01:04 +00:00
|
|
|
usage(TRUE);
|
2004-01-22 19:15:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse command line arguments.
|
|
|
|
*/
|
|
|
|
while (--argc) {
|
|
|
|
char *p = *++argv;
|
|
|
|
if (*p == '-') {
|
|
|
|
/*
|
|
|
|
* An option.
|
|
|
|
*/
|
|
|
|
while (p && *++p) {
|
|
|
|
char c = *p;
|
|
|
|
switch (c) {
|
|
|
|
case '-':
|
|
|
|
/*
|
|
|
|
* Long option.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
char *opt, *val;
|
|
|
|
opt = p++; /* opt will have _one_ leading - */
|
|
|
|
while (*p && *p != '=')
|
|
|
|
p++; /* find end of option */
|
|
|
|
if (*p == '=') {
|
|
|
|
*p++ = '\0';
|
|
|
|
val = p;
|
|
|
|
} else
|
2011-05-07 10:57:19 +00:00
|
|
|
val = NULL;
|
|
|
|
|
2004-01-22 19:15:32 +00:00
|
|
|
if (!strcmp(opt, "-help")) {
|
2011-05-07 10:57:19 +00:00
|
|
|
if (val) {
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: option `-%s'"
|
|
|
|
" expects no argument\n", opt);
|
|
|
|
} else {
|
|
|
|
help();
|
|
|
|
nogo = TRUE;
|
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
} else if (!strcmp(opt, "-version")) {
|
2011-05-07 10:57:19 +00:00
|
|
|
if (val) {
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: option `-%s'"
|
|
|
|
" expects no argument\n", opt);
|
|
|
|
} else {
|
|
|
|
showversion();
|
|
|
|
nogo = TRUE;
|
|
|
|
}
|
2005-03-19 02:26:58 +00:00
|
|
|
} else if (!strcmp(opt, "-pgpfp")) {
|
2011-05-07 10:57:19 +00:00
|
|
|
if (val) {
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: option `-%s'"
|
|
|
|
" expects no argument\n", opt);
|
|
|
|
} else {
|
|
|
|
/* support --pgpfp for consistency */
|
|
|
|
pgp_fingerprints();
|
|
|
|
nogo = TRUE;
|
|
|
|
}
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
} else if (!strcmp(opt, "-old-passphrase")) {
|
|
|
|
if (!val && argc > 1)
|
|
|
|
--argc, val = *++argv;
|
|
|
|
if (!val) {
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: option `-%s'"
|
|
|
|
" expects an argument\n", opt);
|
|
|
|
} else {
|
|
|
|
old_passphrase = readpassphrase(val);
|
|
|
|
if (!old_passphrase)
|
|
|
|
errs = TRUE;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(opt, "-new-passphrase")) {
|
|
|
|
if (!val && argc > 1)
|
|
|
|
--argc, val = *++argv;
|
|
|
|
if (!val) {
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: option `-%s'"
|
|
|
|
" expects an argument\n", opt);
|
|
|
|
} else {
|
|
|
|
new_passphrase = readpassphrase(val);
|
|
|
|
if (!new_passphrase)
|
|
|
|
errs = TRUE;
|
|
|
|
}
|
2016-03-30 07:17:03 +00:00
|
|
|
} else if (!strcmp(opt, "-random-device")) {
|
|
|
|
if (!val && argc > 1)
|
|
|
|
--argc, val = *++argv;
|
|
|
|
if (!val) {
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: option `-%s'"
|
|
|
|
" expects an argument\n", opt);
|
|
|
|
} else {
|
|
|
|
random_device = val;
|
|
|
|
}
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
} else {
|
2004-01-22 19:15:32 +00:00
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr,
|
2006-07-07 14:18:47 +00:00
|
|
|
"puttygen: no such option `-%s'\n", opt);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
p = NULL;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
case 'V':
|
|
|
|
case 'P':
|
|
|
|
case 'l':
|
|
|
|
case 'L':
|
|
|
|
case 'p':
|
|
|
|
case 'q':
|
|
|
|
/*
|
|
|
|
* Option requiring no parameter.
|
|
|
|
*/
|
|
|
|
switch (c) {
|
|
|
|
case 'h':
|
|
|
|
help();
|
|
|
|
nogo = TRUE;
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
showversion();
|
|
|
|
nogo = TRUE;
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
change_passphrase = TRUE;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
outtype = FP;
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
outtype = PUBLICO;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
outtype = PUBLIC;
|
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
progressfn = no_progress;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
case 'b':
|
|
|
|
case 'C':
|
|
|
|
case 'O':
|
|
|
|
case 'o':
|
|
|
|
/*
|
|
|
|
* Option requiring parameter.
|
|
|
|
*/
|
|
|
|
p++;
|
|
|
|
if (!*p && argc > 1)
|
|
|
|
--argc, p = *++argv;
|
|
|
|
else if (!*p) {
|
|
|
|
fprintf(stderr, "puttygen: option `-%c' expects a"
|
|
|
|
" parameter\n", c);
|
|
|
|
errs = TRUE;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Now c is the option and p is the parameter.
|
|
|
|
*/
|
|
|
|
switch (c) {
|
|
|
|
case 't':
|
|
|
|
if (!strcmp(p, "rsa") || !strcmp(p, "rsa2"))
|
|
|
|
keytype = RSA2, sshver = 2;
|
|
|
|
else if (!strcmp(p, "rsa1"))
|
|
|
|
keytype = RSA1, sshver = 1;
|
|
|
|
else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))
|
|
|
|
keytype = DSA, sshver = 2;
|
2014-11-01 09:45:20 +00:00
|
|
|
else if (!strcmp(p, "ecdsa"))
|
|
|
|
keytype = ECDSA, sshver = 2;
|
2015-05-09 14:02:54 +00:00
|
|
|
else if (!strcmp(p, "ed25519"))
|
|
|
|
keytype = ED25519, sshver = 2;
|
2004-01-22 19:15:32 +00:00
|
|
|
else {
|
|
|
|
fprintf(stderr,
|
|
|
|
"puttygen: unknown key type `%s'\n", p);
|
|
|
|
errs = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
bits = atoi(p);
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
comment = p;
|
|
|
|
break;
|
|
|
|
case 'O':
|
|
|
|
if (!strcmp(p, "public"))
|
|
|
|
outtype = PUBLIC;
|
|
|
|
else if (!strcmp(p, "public-openssh"))
|
|
|
|
outtype = PUBLICO;
|
|
|
|
else if (!strcmp(p, "private"))
|
|
|
|
outtype = PRIVATE;
|
|
|
|
else if (!strcmp(p, "fingerprint"))
|
|
|
|
outtype = FP;
|
2015-05-10 06:42:48 +00:00
|
|
|
else if (!strcmp(p, "private-openssh"))
|
|
|
|
outtype = OPENSSH_AUTO, sshver = 2;
|
2015-04-28 18:46:58 +00:00
|
|
|
else if (!strcmp(p, "private-openssh-new"))
|
|
|
|
outtype = OPENSSH_NEW, sshver = 2;
|
2004-01-22 19:15:32 +00:00
|
|
|
else if (!strcmp(p, "private-sshcom"))
|
|
|
|
outtype = SSHCOM, sshver = 2;
|
|
|
|
else {
|
|
|
|
fprintf(stderr,
|
|
|
|
"puttygen: unknown output type `%s'\n", p);
|
|
|
|
errs = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
outfile = p;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p = NULL; /* prevent continued processing */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Unrecognised option.
|
|
|
|
*/
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: no such option `-%c'\n", c);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* A non-option argument.
|
|
|
|
*/
|
|
|
|
if (!infile)
|
|
|
|
infile = p;
|
|
|
|
else {
|
|
|
|
errs = TRUE;
|
|
|
|
fprintf(stderr, "puttygen: cannot handle more than one"
|
|
|
|
" input file\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-09 14:02:47 +00:00
|
|
|
if (bits == -1) {
|
|
|
|
/*
|
|
|
|
* No explicit key size was specified. Default varies
|
|
|
|
* depending on key type.
|
|
|
|
*/
|
|
|
|
switch (keytype) {
|
|
|
|
case ECDSA:
|
|
|
|
bits = 384;
|
|
|
|
break;
|
2015-05-09 14:02:54 +00:00
|
|
|
case ED25519:
|
|
|
|
bits = 256;
|
|
|
|
break;
|
2015-05-09 14:02:47 +00:00
|
|
|
default:
|
2016-04-02 07:00:37 +00:00
|
|
|
bits = DEFAULT_RSADSA_BITS;
|
2015-05-09 14:02:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-01 09:45:20 +00:00
|
|
|
if (keytype == ECDSA && (bits != 256 && bits != 384 && bits != 521)) {
|
|
|
|
fprintf(stderr, "puttygen: invalid bits for ECDSA, choose 256, 384 or 521\n");
|
|
|
|
errs = TRUE;
|
|
|
|
}
|
|
|
|
|
2015-05-09 14:02:54 +00:00
|
|
|
if (keytype == ED25519 && (bits != 256)) {
|
|
|
|
fprintf(stderr, "puttygen: invalid bits for ED25519, choose 256\n");
|
|
|
|
errs = TRUE;
|
|
|
|
}
|
|
|
|
|
2016-03-30 10:41:11 +00:00
|
|
|
if (keytype == RSA2 || keytype == RSA1 || keytype == DSA) {
|
|
|
|
if (bits < 256) {
|
|
|
|
fprintf(stderr, "puttygen: cannot generate %s keys shorter than"
|
|
|
|
" 256 bits\n", (keytype == DSA ? "DSA" : "RSA"));
|
|
|
|
errs = TRUE;
|
2016-04-02 07:00:37 +00:00
|
|
|
} else if (bits < DEFAULT_RSADSA_BITS) {
|
|
|
|
fprintf(stderr, "puttygen: warning: %s keys shorter than"
|
|
|
|
" %d bits are probably not secure\n",
|
|
|
|
(keytype == DSA ? "DSA" : "RSA"), DEFAULT_RSADSA_BITS);
|
|
|
|
/* but this is just a warning, so proceed anyway */
|
2016-03-30 10:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-01-22 19:15:32 +00:00
|
|
|
if (errs)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (nogo)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If run with at least one argument _but_ not the required
|
|
|
|
* ones, print the usage message and return failure.
|
|
|
|
*/
|
|
|
|
if (!infile && keytype == NOKEYGEN) {
|
2006-06-17 13:01:04 +00:00
|
|
|
usage(TRUE);
|
2004-01-22 19:15:32 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
* Figure out further details of exactly what we're going to do.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Bomb out if we've been asked to both load and generate a
|
|
|
|
* key.
|
|
|
|
*/
|
2006-07-07 14:18:47 +00:00
|
|
|
if (keytype != NOKEYGEN && infile) {
|
2004-01-22 19:15:32 +00:00
|
|
|
fprintf(stderr, "puttygen: cannot both load and generate a key\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-07-07 14:18:47 +00:00
|
|
|
/*
|
|
|
|
* We must save the private part when generating a new key.
|
|
|
|
*/
|
|
|
|
if (keytype != NOKEYGEN &&
|
2015-05-10 06:42:48 +00:00
|
|
|
(outtype != PRIVATE && outtype != OPENSSH_AUTO &&
|
2015-04-28 18:46:58 +00:00
|
|
|
outtype != OPENSSH_NEW && outtype != SSHCOM)) {
|
2006-07-07 14:18:47 +00:00
|
|
|
fprintf(stderr, "puttygen: this would generate a new key but "
|
|
|
|
"discard the private part\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2004-01-22 19:15:32 +00:00
|
|
|
/*
|
|
|
|
* Analyse the type of the input file, in case this affects our
|
|
|
|
* course of action.
|
|
|
|
*/
|
|
|
|
if (infile) {
|
|
|
|
infilename = filename_from_str(infile);
|
|
|
|
|
2011-10-02 11:01:57 +00:00
|
|
|
intype = key_type(infilename);
|
2004-01-22 19:15:32 +00:00
|
|
|
|
|
|
|
switch (intype) {
|
|
|
|
case SSH_KEYTYPE_UNOPENABLE:
|
|
|
|
case SSH_KEYTYPE_UNKNOWN:
|
|
|
|
fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
|
|
|
|
infile, key_type_to_str(intype));
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case SSH_KEYTYPE_SSH1:
|
2015-05-12 11:19:57 +00:00
|
|
|
case SSH_KEYTYPE_SSH1_PUBLIC:
|
2004-01-22 19:15:32 +00:00
|
|
|
if (sshver == 2) {
|
2005-03-10 16:36:05 +00:00
|
|
|
fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys"
|
2004-01-22 19:15:32 +00:00
|
|
|
" not supported\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
sshver = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SSH_KEYTYPE_SSH2:
|
2015-05-12 11:19:57 +00:00
|
|
|
case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
|
|
|
|
case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
|
2015-04-28 18:46:58 +00:00
|
|
|
case SSH_KEYTYPE_OPENSSH_PEM:
|
|
|
|
case SSH_KEYTYPE_OPENSSH_NEW:
|
2004-01-22 19:15:32 +00:00
|
|
|
case SSH_KEYTYPE_SSHCOM:
|
|
|
|
if (sshver == 1) {
|
2005-03-10 16:36:05 +00:00
|
|
|
fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys"
|
2004-01-22 19:15:32 +00:00
|
|
|
" not supported\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
sshver = 2;
|
|
|
|
break;
|
2015-05-10 06:42:48 +00:00
|
|
|
|
|
|
|
case SSH_KEYTYPE_OPENSSH_AUTO:
|
|
|
|
default:
|
|
|
|
assert(0 && "Should never see these types on an input file");
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the default output file, if none is provided.
|
|
|
|
*
|
|
|
|
* This will usually be equal to stdout, except that if the
|
|
|
|
* input and output file formats are the same then the default
|
|
|
|
* output is to overwrite the input.
|
|
|
|
*
|
|
|
|
* Also in this code, we bomb out if the input and output file
|
|
|
|
* formats are the same and no other action is performed.
|
|
|
|
*/
|
|
|
|
if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) ||
|
|
|
|
(intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) ||
|
2015-05-10 06:42:48 +00:00
|
|
|
(intype == SSH_KEYTYPE_OPENSSH_PEM && outtype == OPENSSH_AUTO) ||
|
2015-04-28 18:46:58 +00:00
|
|
|
(intype == SSH_KEYTYPE_OPENSSH_NEW && outtype == OPENSSH_NEW) ||
|
2004-01-22 19:15:32 +00:00
|
|
|
(intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {
|
|
|
|
if (!outfile) {
|
|
|
|
outfile = infile;
|
2004-01-24 17:16:37 +00:00
|
|
|
outfiletmp = dupcat(outfile, ".tmp", NULL);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!change_passphrase && !comment) {
|
|
|
|
fprintf(stderr, "puttygen: this command would perform no useful"
|
|
|
|
" action\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!outfile) {
|
|
|
|
/*
|
|
|
|
* Bomb out rather than automatically choosing to write
|
|
|
|
* a private key file to stdout.
|
|
|
|
*/
|
2015-05-10 06:42:48 +00:00
|
|
|
if (outtype == PRIVATE || outtype == OPENSSH_AUTO ||
|
2015-04-28 18:46:58 +00:00
|
|
|
outtype == OPENSSH_NEW || outtype == SSHCOM) {
|
2004-01-22 19:15:32 +00:00
|
|
|
fprintf(stderr, "puttygen: need to specify an output file\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Figure out whether we need to load the encrypted part of the
|
|
|
|
* key. This will be the case if either (a) we need to write
|
|
|
|
* out a private key format, or (b) the entire input key file
|
|
|
|
* is encrypted.
|
|
|
|
*/
|
2015-05-10 06:42:48 +00:00
|
|
|
if (outtype == PRIVATE || outtype == OPENSSH_AUTO ||
|
2015-04-28 18:46:58 +00:00
|
|
|
outtype == OPENSSH_NEW || outtype == SSHCOM ||
|
|
|
|
intype == SSH_KEYTYPE_OPENSSH_PEM ||
|
|
|
|
intype == SSH_KEYTYPE_OPENSSH_NEW ||
|
|
|
|
intype == SSH_KEYTYPE_SSHCOM)
|
2004-01-22 19:15:32 +00:00
|
|
|
load_encrypted = TRUE;
|
|
|
|
else
|
|
|
|
load_encrypted = FALSE;
|
|
|
|
|
2015-05-12 11:19:57 +00:00
|
|
|
if (load_encrypted && (intype == SSH_KEYTYPE_SSH1_PUBLIC ||
|
|
|
|
intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
|
|
|
|
intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)) {
|
|
|
|
fprintf(stderr, "puttygen: cannot perform this action on a "
|
|
|
|
"public-key-only input file\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2004-01-22 19:15:32 +00:00
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
* Now we're ready to actually do some stuff.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Either load or generate a key.
|
|
|
|
*/
|
|
|
|
if (keytype != NOKEYGEN) {
|
|
|
|
char *entropy;
|
|
|
|
char default_comment[80];
|
2005-01-09 14:27:48 +00:00
|
|
|
struct tm tm;
|
2004-01-22 19:15:32 +00:00
|
|
|
struct progress prog;
|
|
|
|
|
|
|
|
prog.phase = -1;
|
|
|
|
prog.current = -1;
|
|
|
|
|
2005-01-09 14:27:48 +00:00
|
|
|
tm = ltime();
|
2004-01-22 19:15:32 +00:00
|
|
|
if (keytype == DSA)
|
2005-01-09 14:27:48 +00:00
|
|
|
strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);
|
2014-11-01 09:45:20 +00:00
|
|
|
else if (keytype == ECDSA)
|
|
|
|
strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm);
|
2015-05-09 14:02:54 +00:00
|
|
|
else if (keytype == ED25519)
|
|
|
|
strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm);
|
2004-01-22 19:15:32 +00:00
|
|
|
else
|
2005-01-09 14:27:48 +00:00
|
|
|
strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);
|
2004-01-22 19:15:32 +00:00
|
|
|
|
2004-11-27 19:56:38 +00:00
|
|
|
random_ref();
|
2016-03-30 07:17:03 +00:00
|
|
|
entropy = get_random_data(bits / 8, random_device);
|
2007-03-13 14:43:14 +00:00
|
|
|
if (!entropy) {
|
|
|
|
fprintf(stderr, "puttygen: failed to collect entropy, "
|
|
|
|
"could not generate key\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
random_add_heavynoise(entropy, bits / 8);
|
2012-07-22 19:51:50 +00:00
|
|
|
smemclr(entropy, bits/8);
|
2004-01-22 19:15:32 +00:00
|
|
|
sfree(entropy);
|
|
|
|
|
|
|
|
if (keytype == DSA) {
|
|
|
|
struct dss_key *dsskey = snew(struct dss_key);
|
|
|
|
dsa_generate(dsskey, bits, progressfn, &prog);
|
|
|
|
ssh2key = snew(struct ssh2_userkey);
|
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
|
|
|
ssh2key->data = &dsskey->sshk;
|
2004-01-22 19:15:32 +00:00
|
|
|
ssh2key->alg = &ssh_dss;
|
|
|
|
ssh1key = NULL;
|
2014-11-01 09:45:20 +00:00
|
|
|
} else if (keytype == ECDSA) {
|
|
|
|
struct ec_key *ec = snew(struct ec_key);
|
|
|
|
ec_generate(ec, bits, progressfn, &prog);
|
|
|
|
ssh2key = snew(struct ssh2_userkey);
|
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
|
|
|
ssh2key->data = &ec->sshk;
|
2015-05-15 09:13:05 +00:00
|
|
|
ssh2key->alg = ec->signalg;
|
2014-11-01 09:45:20 +00:00
|
|
|
ssh1key = NULL;
|
2015-05-09 14:02:54 +00:00
|
|
|
} else if (keytype == ED25519) {
|
|
|
|
struct ec_key *ec = snew(struct ec_key);
|
|
|
|
ec_edgenerate(ec, bits, progressfn, &prog);
|
|
|
|
ssh2key = snew(struct ssh2_userkey);
|
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
|
|
|
ssh2key->data = &ec->sshk;
|
2015-05-09 14:02:54 +00:00
|
|
|
ssh2key->alg = &ssh_ecdsa_ed25519;
|
|
|
|
ssh1key = NULL;
|
2004-01-22 19:15:32 +00:00
|
|
|
} else {
|
|
|
|
struct RSAKey *rsakey = snew(struct RSAKey);
|
|
|
|
rsa_generate(rsakey, bits, progressfn, &prog);
|
2004-01-24 17:16:37 +00:00
|
|
|
rsakey->comment = NULL;
|
2004-01-22 19:15:32 +00:00
|
|
|
if (keytype == RSA1) {
|
|
|
|
ssh1key = rsakey;
|
|
|
|
} else {
|
|
|
|
ssh2key = snew(struct ssh2_userkey);
|
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
|
|
|
ssh2key->data = &rsakey->sshk;
|
2004-01-22 19:15:32 +00:00
|
|
|
ssh2key->alg = &ssh_rsa;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
progressfn(&prog, PROGFN_PROGRESS, INT_MAX, -1);
|
|
|
|
|
|
|
|
if (ssh2key)
|
|
|
|
ssh2key->comment = dupstr(default_comment);
|
|
|
|
if (ssh1key)
|
|
|
|
ssh1key->comment = dupstr(default_comment);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
const char *error = NULL;
|
|
|
|
int encrypted;
|
|
|
|
|
|
|
|
assert(infile != NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find out whether the input key is encrypted.
|
|
|
|
*/
|
|
|
|
if (intype == SSH_KEYTYPE_SSH1)
|
2018-05-24 07:22:44 +00:00
|
|
|
encrypted = rsa_ssh1_encrypted(infilename, &origcomment);
|
2004-01-22 19:15:32 +00:00
|
|
|
else if (intype == SSH_KEYTYPE_SSH2)
|
2011-10-02 11:01:57 +00:00
|
|
|
encrypted = ssh2_userkey_encrypted(infilename, &origcomment);
|
2004-01-22 19:15:32 +00:00
|
|
|
else
|
2011-10-02 11:01:57 +00:00
|
|
|
encrypted = import_encrypted(infilename, intype, &origcomment);
|
2004-01-22 19:15:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If so, ask for a passphrase.
|
|
|
|
*/
|
|
|
|
if (encrypted && load_encrypted) {
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
if (!old_passphrase) {
|
|
|
|
prompts_t *p = new_prompts(NULL);
|
|
|
|
int ret;
|
|
|
|
p->to_server = FALSE;
|
|
|
|
p->name = dupstr("SSH key passphrase");
|
|
|
|
add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE);
|
2018-05-18 06:22:56 +00:00
|
|
|
ret = console_get_userpass_input(p);
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
assert(ret >= 0);
|
|
|
|
if (!ret) {
|
|
|
|
free_prompts(p);
|
|
|
|
perror("puttygen: unable to read passphrase");
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
old_passphrase = dupstr(p->prompts[0]->result);
|
|
|
|
free_prompts(p);
|
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
} else {
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
old_passphrase = NULL;
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (intype) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
case SSH_KEYTYPE_SSH1:
|
2015-05-12 11:19:57 +00:00
|
|
|
case SSH_KEYTYPE_SSH1_PUBLIC:
|
2004-01-22 19:15:32 +00:00
|
|
|
ssh1key = snew(struct RSAKey);
|
|
|
|
if (!load_encrypted) {
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *blob;
|
2018-05-29 19:36:21 +00:00
|
|
|
BinarySource src[1];
|
2004-01-22 19:15:32 +00:00
|
|
|
|
2018-05-24 09:59:39 +00:00
|
|
|
blob = strbuf_new();
|
|
|
|
ret = rsa_ssh1_loadpub(infilename, BinarySink_UPCAST(blob),
|
2018-05-24 07:22:44 +00:00
|
|
|
&origcomment, &error);
|
2018-05-29 19:36:21 +00:00
|
|
|
BinarySource_BARE_INIT(src, blob->u, blob->len);
|
|
|
|
get_rsa_ssh1_pub(src, ssh1key, NULL, RSA_SSH1_EXPONENT_FIRST);
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(blob);
|
|
|
|
|
2005-10-30 19:13:43 +00:00
|
|
|
ssh1key->comment = dupstr(origcomment);
|
2004-01-24 17:16:37 +00:00
|
|
|
ssh1key->private_exponent = NULL;
|
2012-11-13 21:34:12 +00:00
|
|
|
ssh1key->p = NULL;
|
|
|
|
ssh1key->q = NULL;
|
|
|
|
ssh1key->iqmp = NULL;
|
2004-01-22 19:15:32 +00:00
|
|
|
} else {
|
2018-05-24 07:22:44 +00:00
|
|
|
ret = rsa_ssh1_loadkey(
|
|
|
|
infilename, ssh1key, old_passphrase, &error);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
2004-01-24 17:16:37 +00:00
|
|
|
if (ret > 0)
|
2004-01-22 19:15:32 +00:00
|
|
|
error = NULL;
|
|
|
|
else if (!error)
|
|
|
|
error = "unknown error";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SSH_KEYTYPE_SSH2:
|
2015-05-12 11:19:57 +00:00
|
|
|
case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
|
|
|
|
case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
|
2004-01-22 19:15:32 +00:00
|
|
|
if (!load_encrypted) {
|
2018-05-24 09:59:39 +00:00
|
|
|
ssh2blob = strbuf_new();
|
|
|
|
if (ssh2_userkey_loadpub(infilename, &ssh2alg, BinarySink_UPCAST(ssh2blob),
|
|
|
|
&origcomment, &error)) {
|
2013-02-22 21:39:02 +00:00
|
|
|
ssh2algf = find_pubkey_alg(ssh2alg);
|
|
|
|
if (ssh2algf)
|
Clean up ssh_keyalg APIs and implementations.
Quite a few of the function pointers in the ssh_keyalg vtable now take
ptrlen arguments in place of separate pointer and length pairs.
Meanwhile, the various key types' implementations of those functions
now work by initialising a BinarySource with the input ptrlen and
using the new decode functions to walk along it.
One exception is the openssh_createkey method which reads a private
key in the wire format used by OpenSSH's SSH-2 agent protocol, which
has to consume a prefix of a larger data stream, and tell the caller
how much of that data was the private key. That function now takes an
actual BinarySource, and passes that directly to the decode functions,
so that on return the caller finds that the BinarySource's read
pointer has been advanced exactly past the private key.
This let me throw away _several_ reimplementations of mpint-reading
functions, one in each of sshrsa, sshdss.c and sshecc.c. Worse still,
they didn't all have exactly the SSH-2 semantics, because the thing in
sshrsa.c whose name suggested it was an mpint-reading function
actually tolerated the wrong number of leading zero bytes, which it
had to be able to do to cope with the "ssh-rsa" signature format which
contains a thing that isn't quite an SSH-2 mpint. Now that deviation
is clearly commented!
2018-05-31 17:40:51 +00:00
|
|
|
bits = ssh2algf->pubkey_bits(
|
|
|
|
ssh2algf, make_ptrlen(ssh2blob->s, ssh2blob->len));
|
2013-02-22 21:39:02 +00:00
|
|
|
else
|
|
|
|
bits = -1;
|
2018-05-24 09:59:39 +00:00
|
|
|
} else {
|
|
|
|
strbuf_free(ssh2blob);
|
2013-02-22 21:39:02 +00:00
|
|
|
}
|
2015-05-15 09:12:06 +00:00
|
|
|
sfree(ssh2alg);
|
2004-01-22 19:15:32 +00:00
|
|
|
} else {
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
ssh2key = ssh2_load_userkey(infilename, old_passphrase,
|
|
|
|
&error);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
2004-01-24 17:16:37 +00:00
|
|
|
if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)
|
2004-01-22 19:15:32 +00:00
|
|
|
error = NULL;
|
|
|
|
else if (!error) {
|
|
|
|
if (ssh2key == SSH2_WRONG_PASSPHRASE)
|
|
|
|
error = "wrong passphrase";
|
|
|
|
else
|
|
|
|
error = "unknown error";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-04-28 18:46:58 +00:00
|
|
|
case SSH_KEYTYPE_OPENSSH_PEM:
|
|
|
|
case SSH_KEYTYPE_OPENSSH_NEW:
|
2004-01-22 19:15:32 +00:00
|
|
|
case SSH_KEYTYPE_SSHCOM:
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
ssh2key = import_ssh2(infilename, intype, old_passphrase, &error);
|
2005-02-27 23:01:11 +00:00
|
|
|
if (ssh2key) {
|
|
|
|
if (ssh2key != SSH2_WRONG_PASSPHRASE)
|
|
|
|
error = NULL;
|
2004-01-22 19:15:32 +00:00
|
|
|
else
|
2005-02-27 23:01:11 +00:00
|
|
|
error = "wrong passphrase";
|
|
|
|
} else if (!error)
|
|
|
|
error = "unknown error";
|
2004-01-22 19:15:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
fprintf(stderr, "puttygen: error loading `%s': %s\n",
|
|
|
|
infile, error);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change the comment if asked to.
|
|
|
|
*/
|
|
|
|
if (comment) {
|
|
|
|
if (sshver == 1) {
|
|
|
|
assert(ssh1key);
|
|
|
|
sfree(ssh1key->comment);
|
|
|
|
ssh1key->comment = dupstr(comment);
|
|
|
|
} else {
|
|
|
|
assert(ssh2key);
|
|
|
|
sfree(ssh2key->comment);
|
|
|
|
ssh2key->comment = dupstr(comment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
/*
|
|
|
|
* Unless we're changing the passphrase, the old one (if any) is a
|
|
|
|
* reasonable default.
|
|
|
|
*/
|
|
|
|
if (!change_passphrase && old_passphrase && !new_passphrase)
|
|
|
|
new_passphrase = dupstr(old_passphrase);
|
|
|
|
|
2004-01-22 19:15:32 +00:00
|
|
|
/*
|
|
|
|
* Prompt for a new passphrase if we have been asked to, or if
|
|
|
|
* we have just generated a key.
|
|
|
|
*/
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
if (!new_passphrase && (change_passphrase || keytype != NOKEYGEN)) {
|
2005-10-30 20:24:09 +00:00
|
|
|
prompts_t *p = new_prompts(NULL);
|
|
|
|
int ret;
|
2004-01-22 19:15:32 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
p->to_server = FALSE;
|
|
|
|
p->name = dupstr("New SSH key passphrase");
|
2011-10-02 11:50:45 +00:00
|
|
|
add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE);
|
|
|
|
add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE);
|
2018-05-18 06:22:56 +00:00
|
|
|
ret = console_get_userpass_input(p);
|
2005-10-30 20:24:09 +00:00
|
|
|
assert(ret >= 0);
|
|
|
|
if (!ret) {
|
|
|
|
free_prompts(p);
|
2004-01-22 19:15:32 +00:00
|
|
|
perror("puttygen: unable to read new passphrase");
|
|
|
|
return 1;
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
|
|
|
if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) {
|
|
|
|
free_prompts(p);
|
|
|
|
fprintf(stderr, "puttygen: passphrases do not match\n");
|
|
|
|
return 1;
|
|
|
|
}
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
new_passphrase = dupstr(p->prompts[0]->result);
|
2005-10-30 20:24:09 +00:00
|
|
|
free_prompts(p);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
}
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
if (new_passphrase && !*new_passphrase) {
|
|
|
|
sfree(new_passphrase);
|
|
|
|
new_passphrase = NULL;
|
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write output.
|
|
|
|
*
|
|
|
|
* (In the case where outfile and outfiletmp are both NULL,
|
|
|
|
* there is no semantic reason to initialise outfilename at
|
|
|
|
* all; but we have to write _something_ to it or some compiler
|
|
|
|
* will probably complain that it might be used uninitialised.)
|
|
|
|
*/
|
|
|
|
if (outfiletmp)
|
|
|
|
outfilename = filename_from_str(outfiletmp);
|
|
|
|
else
|
|
|
|
outfilename = filename_from_str(outfile ? outfile : "");
|
|
|
|
|
|
|
|
switch (outtype) {
|
2015-04-28 18:46:08 +00:00
|
|
|
int ret, real_outtype;
|
2004-01-22 19:15:32 +00:00
|
|
|
|
|
|
|
case PRIVATE:
|
|
|
|
if (sshver == 1) {
|
|
|
|
assert(ssh1key);
|
2018-05-24 07:22:44 +00:00
|
|
|
ret = rsa_ssh1_savekey(outfilename, ssh1key, new_passphrase);
|
2004-01-22 19:15:32 +00:00
|
|
|
if (!ret) {
|
2005-03-10 16:36:05 +00:00
|
|
|
fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");
|
2004-01-22 19:15:32 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert(ssh2key);
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
ret = ssh2_save_userkey(outfilename, ssh2key, new_passphrase);
|
2004-01-22 19:15:32 +00:00
|
|
|
if (!ret) {
|
2005-03-10 16:36:05 +00:00
|
|
|
fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");
|
2004-01-22 19:15:32 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2004-01-24 17:16:37 +00:00
|
|
|
if (outfiletmp) {
|
|
|
|
if (!move(outfiletmp, outfile))
|
|
|
|
return 1; /* rename failed */
|
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PUBLIC:
|
|
|
|
case PUBLICO:
|
2015-05-12 12:42:26 +00:00
|
|
|
{
|
|
|
|
FILE *fp;
|
2004-01-22 19:15:32 +00:00
|
|
|
|
2015-05-12 12:42:26 +00:00
|
|
|
if (outfile)
|
|
|
|
fp = f_open(outfilename, "w", FALSE);
|
|
|
|
else
|
|
|
|
fp = stdout;
|
2004-01-22 19:15:32 +00:00
|
|
|
|
2015-05-12 12:42:26 +00:00
|
|
|
if (sshver == 1) {
|
|
|
|
ssh1_write_pubkey(fp, ssh1key);
|
|
|
|
} else {
|
|
|
|
if (!ssh2blob) {
|
|
|
|
assert(ssh2key);
|
2018-05-24 09:59:39 +00:00
|
|
|
ssh2blob = strbuf_new();
|
|
|
|
ssh2key->alg->public_blob(ssh2key->data, BinarySink_UPCAST(ssh2blob));
|
2015-05-12 12:42:26 +00:00
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
|
2015-05-12 12:42:26 +00:00
|
|
|
ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment,
|
2018-05-24 09:59:39 +00:00
|
|
|
ssh2blob->s, ssh2blob->len,
|
2015-05-12 12:42:26 +00:00
|
|
|
(outtype == PUBLIC ?
|
|
|
|
SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 :
|
|
|
|
SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH));
|
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
|
|
|
|
if (outfile)
|
|
|
|
fclose(fp);
|
2015-05-12 12:42:26 +00:00
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FP:
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
char *fingerprint;
|
|
|
|
|
|
|
|
if (sshver == 1) {
|
|
|
|
assert(ssh1key);
|
2018-06-03 07:08:53 +00:00
|
|
|
fingerprint = rsa_ssh1_fingerprint(ssh1key);
|
2004-01-22 19:15:32 +00:00
|
|
|
} else {
|
|
|
|
if (ssh2key) {
|
2015-05-12 13:35:44 +00:00
|
|
|
fingerprint = ssh2_fingerprint(ssh2key->alg,
|
|
|
|
ssh2key->data);
|
2004-01-22 19:15:32 +00:00
|
|
|
} else {
|
|
|
|
assert(ssh2blob);
|
2018-05-24 09:59:39 +00:00
|
|
|
fingerprint = ssh2_fingerprint_blob(
|
|
|
|
ssh2blob->s, ssh2blob->len);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (outfile)
|
2007-01-09 18:14:30 +00:00
|
|
|
fp = f_open(outfilename, "w", FALSE);
|
2004-01-22 19:15:32 +00:00
|
|
|
else
|
|
|
|
fp = stdout;
|
|
|
|
fprintf(fp, "%s\n", fingerprint);
|
|
|
|
if (outfile)
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
sfree(fingerprint);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-05-10 06:42:48 +00:00
|
|
|
case OPENSSH_AUTO:
|
2015-04-28 18:46:58 +00:00
|
|
|
case OPENSSH_NEW:
|
2004-01-22 19:15:32 +00:00
|
|
|
case SSHCOM:
|
|
|
|
assert(sshver == 2);
|
|
|
|
assert(ssh2key);
|
2014-01-16 19:16:19 +00:00
|
|
|
random_ref(); /* both foreign key types require randomness,
|
|
|
|
* for IV or padding */
|
2015-04-28 18:46:08 +00:00
|
|
|
switch (outtype) {
|
2015-05-10 06:42:48 +00:00
|
|
|
case OPENSSH_AUTO:
|
|
|
|
real_outtype = SSH_KEYTYPE_OPENSSH_AUTO;
|
2015-04-28 18:46:58 +00:00
|
|
|
break;
|
|
|
|
case OPENSSH_NEW:
|
|
|
|
real_outtype = SSH_KEYTYPE_OPENSSH_NEW;
|
2015-04-28 18:46:08 +00:00
|
|
|
break;
|
|
|
|
case SSHCOM:
|
|
|
|
real_outtype = SSH_KEYTYPE_SSHCOM;
|
|
|
|
break;
|
2015-04-28 18:46:58 +00:00
|
|
|
default:
|
|
|
|
assert(0 && "control flow goof");
|
2015-04-28 18:46:08 +00:00
|
|
|
}
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase);
|
2004-01-22 19:15:32 +00:00
|
|
|
if (!ret) {
|
|
|
|
fprintf(stderr, "puttygen: unable to export key\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2004-01-24 17:16:37 +00:00
|
|
|
if (outfiletmp) {
|
|
|
|
if (!move(outfiletmp, outfile))
|
|
|
|
return 1; /* rename failed */
|
|
|
|
}
|
2004-01-22 19:15:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Add command-line passphrase-file options to command-line PuTTYgen.
Patch due to Colin Watson.
Putting the passphrase in a file avoids exposing it to 'ps' which can
print out every process's command line, while at the same time not
being as platform-specific as the approach of providing an fd number
(since cmdgen.c is in principle a potential cross-platform PuTTYgen,
not just a Unix one, which is why it's not in the 'unix' directory).
Of course it introduces its own risks if someone can read the file
from your disk after you delete it; probably the best approach to
avoiding this, if possible, is to point the option at a file on an
in-memory tmpfs type file system. Or better still, use bash-style
/dev/fd options such as
puttygen --new-passphrase <(echo -n "my passphrase") [options]
Failing that, try a secure file-wipe utility, as the man page change
mentions.
(And a use case not to be overlooked, of course, is the one where you
actually want to generate an unprotected key - in which case, just
pass /dev/null as the filename.)
2016-03-17 18:42:46 +00:00
|
|
|
if (old_passphrase) {
|
|
|
|
smemclr(old_passphrase, strlen(old_passphrase));
|
|
|
|
sfree(old_passphrase);
|
|
|
|
}
|
|
|
|
if (new_passphrase) {
|
|
|
|
smemclr(new_passphrase, strlen(new_passphrase));
|
|
|
|
sfree(new_passphrase);
|
2004-01-22 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ssh1key)
|
|
|
|
freersakey(ssh1key);
|
|
|
|
if (ssh2key) {
|
|
|
|
ssh2key->alg->freekey(ssh2key->data);
|
|
|
|
sfree(ssh2key);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2004-01-24 17:16:37 +00:00
|
|
|
|
|
|
|
#ifdef TEST_CMDGEN
|
|
|
|
|
|
|
|
#undef main
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
|
|
int passes, fails;
|
|
|
|
|
|
|
|
void setup_passphrases(char *first, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char *next;
|
|
|
|
|
|
|
|
nprompts = 0;
|
|
|
|
if (first) {
|
|
|
|
prompts[nprompts++] = first;
|
|
|
|
va_start(ap, first);
|
|
|
|
while ((next = va_arg(ap, char *)) != NULL) {
|
|
|
|
assert(nprompts < lenof(prompts));
|
|
|
|
prompts[nprompts++] = next;
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void test(int retval, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int i, argc, ret;
|
|
|
|
char **argv;
|
|
|
|
|
|
|
|
argc = 0;
|
|
|
|
va_start(ap, retval);
|
|
|
|
while (va_arg(ap, char *) != NULL)
|
|
|
|
argc++;
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
argv = snewn(argc+1, char *);
|
|
|
|
va_start(ap, retval);
|
|
|
|
for (i = 0; i <= argc; i++)
|
|
|
|
argv[i] = va_arg(ap, char *);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
promptsgot = 0;
|
|
|
|
ret = cmdgen_main(argc, argv);
|
|
|
|
|
|
|
|
if (ret != retval) {
|
|
|
|
printf("FAILED retval (exp %d got %d):", retval, ret);
|
|
|
|
for (i = 0; i < argc; i++)
|
|
|
|
printf(" %s", argv[i]);
|
|
|
|
printf("\n");
|
|
|
|
fails++;
|
|
|
|
} else if (promptsgot != nprompts) {
|
|
|
|
printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot);
|
|
|
|
for (i = 0; i < argc; i++)
|
|
|
|
printf(" %s", argv[i]);
|
|
|
|
printf("\n");
|
|
|
|
fails++;
|
|
|
|
} else {
|
|
|
|
passes++;
|
|
|
|
}
|
2017-02-14 20:42:26 +00:00
|
|
|
|
|
|
|
sfree(argv);
|
2004-01-24 17:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void filecmp(char *file1, char *file2, char *fmt, ...)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Ideally I should do file comparison myself, to maximise the
|
|
|
|
* portability of this test suite once this application begins
|
|
|
|
* running on non-Unix platforms. For the moment, though,
|
|
|
|
* calling Unix diff is perfectly adequate.
|
|
|
|
*/
|
|
|
|
char *buf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
buf = dupprintf("diff -q '%s' '%s'", file1, file2);
|
|
|
|
ret = system(buf);
|
|
|
|
sfree(buf);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
printf("FAILED diff (ret=%d): ", ret);
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vprintf(fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
fails++;
|
|
|
|
} else
|
|
|
|
passes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *cleanup_fp(char *s)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (!strncmp(s, "ssh-", 4)) {
|
|
|
|
s += strcspn(s, " \n\t");
|
|
|
|
s += strspn(s, " \n\t");
|
|
|
|
}
|
|
|
|
|
|
|
|
p = s;
|
|
|
|
s += strcspn(s, " \n\t");
|
|
|
|
s += strspn(s, " \n\t");
|
|
|
|
s += strcspn(s, " \n\t");
|
|
|
|
|
2016-03-30 07:25:25 +00:00
|
|
|
return dupprintf("%.*s", (int)(s - p), p);
|
2004-01-24 17:16:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *get_fp(char *filename)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
char buf[256], *ret;
|
|
|
|
|
|
|
|
fp = fopen(filename, "r");
|
|
|
|
if (!fp)
|
|
|
|
return NULL;
|
|
|
|
ret = fgets(buf, sizeof(buf), fp);
|
|
|
|
fclose(fp);
|
|
|
|
if (!ret)
|
|
|
|
return NULL;
|
|
|
|
return cleanup_fp(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void check_fp(char *filename, char *fp, char *fmt, ...)
|
|
|
|
{
|
|
|
|
char *newfp;
|
|
|
|
|
|
|
|
if (!fp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
newfp = get_fp(filename);
|
|
|
|
|
|
|
|
if (!strcmp(fp, newfp)) {
|
|
|
|
passes++;
|
|
|
|
} else {
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp);
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vprintf(fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
fails++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sfree(newfp);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
static char *const keytypes[] = { "rsa1", "dsa", "rsa" };
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Even when this thing is compiled for automatic test mode,
|
|
|
|
* it's helpful to be able to invoke it with command-line
|
|
|
|
* options for _manual_ tests.
|
|
|
|
*/
|
|
|
|
if (argc > 1)
|
|
|
|
return cmdgen_main(argc, argv);
|
|
|
|
|
|
|
|
passes = fails = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < lenof(keytypes); i++) {
|
|
|
|
char filename[128], osfilename[128], scfilename[128];
|
|
|
|
char pubfilename[128], tmpfilename1[128], tmpfilename2[128];
|
|
|
|
char *fp;
|
|
|
|
|
|
|
|
sprintf(filename, "test-%s.ppk", keytypes[i]);
|
|
|
|
sprintf(pubfilename, "test-%s.pub", keytypes[i]);
|
|
|
|
sprintf(osfilename, "test-%s.os", keytypes[i]);
|
|
|
|
sprintf(scfilename, "test-%s.sc", keytypes[i]);
|
|
|
|
sprintf(tmpfilename1, "test-%s.tmp1", keytypes[i]);
|
|
|
|
sprintf(tmpfilename2, "test-%s.tmp2", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create an encrypted key.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge", "sponge", NULL);
|
|
|
|
test(0, "puttygen", "-t", keytypes[i], "-o", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public key in OpenSSH format.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL);
|
|
|
|
{
|
|
|
|
char cmdbuf[256];
|
|
|
|
fp = NULL;
|
|
|
|
sprintf(cmdbuf, "ssh-keygen -l -f '%s' > '%s'",
|
|
|
|
pubfilename, tmpfilename1);
|
|
|
|
if (system(cmdbuf) ||
|
|
|
|
(fp = get_fp(tmpfilename1)) == NULL) {
|
|
|
|
printf("UNABLE to test fingerprint matching against OpenSSH");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public key in IETF/ssh.com format.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-p", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the fingerprint of the key.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-l", filename, "-o", tmpfilename1, NULL);
|
|
|
|
if (!fp) {
|
|
|
|
/*
|
|
|
|
* If we can't test fingerprints against OpenSSH, we
|
|
|
|
* can at the very least test equality of all the
|
|
|
|
* fingerprints we generate of this key throughout
|
|
|
|
* testing.
|
|
|
|
*/
|
|
|
|
fp = get_fp(tmpfilename1);
|
|
|
|
} else {
|
|
|
|
check_fp(tmpfilename1, fp, "%s initial fp", keytypes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change the comment of the key; this _does_ require a
|
|
|
|
* passphrase owing to the tamperproofing.
|
|
|
|
*
|
2005-03-10 16:36:05 +00:00
|
|
|
* NOTE: In SSH-1, this only requires a passphrase because
|
2004-01-24 17:16:37 +00:00
|
|
|
* of inadequacies of the loading and saving mechanisms. In
|
|
|
|
* _principle_, it should be perfectly possible to modify
|
2005-03-10 16:36:05 +00:00
|
|
|
* the comment on an SSH-1 key without requiring a
|
2004-01-24 17:16:37 +00:00
|
|
|
* passphrase; the only reason I can't do it is because my
|
|
|
|
* loading and saving mechanisms don't include a method of
|
|
|
|
* loading all the key data without also trying to decrypt
|
|
|
|
* the private section.
|
|
|
|
*
|
|
|
|
* I don't consider this to be a problem worth solving,
|
|
|
|
* because (a) to fix it would probably end up bloating
|
2005-03-10 16:36:05 +00:00
|
|
|
* PuTTY proper, and (b) SSH-1 is on the way out anyway so
|
2004-01-24 17:16:37 +00:00
|
|
|
* it shouldn't be highly significant. If it seriously
|
|
|
|
* bothers anyone then perhaps I _might_ be persuadable.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge", NULL);
|
|
|
|
test(0, "puttygen", "-C", "new-comment", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change the passphrase to nothing.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge", "", "", NULL);
|
|
|
|
test(0, "puttygen", "-P", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change the comment of the key again; this time we expect no
|
|
|
|
* passphrase to be required.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-C", "new-comment-2", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Export the private key into OpenSSH format; no passphrase
|
|
|
|
* should be required since the key is currently unencrypted.
|
|
|
|
* For RSA1 keys, this should give an error.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,
|
|
|
|
filename, NULL);
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
/*
|
|
|
|
* List the fingerprint of the OpenSSH-formatted key.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
|
|
|
|
check_fp(tmpfilename1, fp, "%s openssh clear fp", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the OpenSSH-formatted key in
|
|
|
|
* OpenSSH format.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-L", osfilename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the OpenSSH-formatted key in
|
|
|
|
* IETF/ssh.com format.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-p", osfilename, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Export the private key into ssh.com format; no passphrase
|
|
|
|
* should be required since the key is currently unencrypted.
|
|
|
|
* For RSA1 keys, this should give an error.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,
|
|
|
|
filename, NULL);
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
/*
|
|
|
|
* List the fingerprint of the ssh.com-formatted key.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
|
|
|
|
check_fp(tmpfilename1, fp, "%s ssh.com clear fp", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the ssh.com-formatted key in
|
|
|
|
* OpenSSH format.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-L", scfilename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the ssh.com-formatted key in
|
|
|
|
* IETF/ssh.com format.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-p", scfilename, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
/*
|
|
|
|
* Convert from OpenSSH into ssh.com.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", osfilename, "-o", tmpfilename1,
|
|
|
|
"-O", "private-sshcom", NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert from ssh.com back into a PuTTY key,
|
|
|
|
* supplying the same comment as we had before we
|
|
|
|
* started to ensure the comparison works.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
|
|
|
"-o", tmpfilename2, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if the PuTTY key thus generated is the same as
|
|
|
|
* the original.
|
|
|
|
*/
|
|
|
|
filecmp(filename, tmpfilename2,
|
|
|
|
"p->o->s->p clear %s", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert from ssh.com to OpenSSH.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", scfilename, "-o", tmpfilename1,
|
|
|
|
"-O", "private-openssh", NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert from OpenSSH back into a PuTTY key,
|
|
|
|
* supplying the same comment as we had before we
|
|
|
|
* started to ensure the comparison works.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
|
|
|
"-o", tmpfilename2, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if the PuTTY key thus generated is the same as
|
|
|
|
* the original.
|
|
|
|
*/
|
|
|
|
filecmp(filename, tmpfilename2,
|
|
|
|
"p->s->o->p clear %s", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, do a round-trip conversion between PuTTY
|
|
|
|
* and ssh.com without involving OpenSSH, to test that
|
|
|
|
* the key comment is preserved in that case.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
|
|
|
|
filename, NULL);
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
|
|
|
|
filecmp(filename, tmpfilename2,
|
|
|
|
"p->s->p clear %s", keytypes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that mismatched passphrases cause an error.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", "sponge3", NULL);
|
|
|
|
test(1, "puttygen", "-P", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Put a passphrase back on.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", "sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-P", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Export the private key into OpenSSH format, this time
|
|
|
|
* while encrypted. For RSA1 keys, this should give an
|
|
|
|
* error.
|
|
|
|
*/
|
|
|
|
if (i == 0)
|
|
|
|
setup_passphrases(NULL); /* error, hence no passphrase read */
|
|
|
|
else
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,
|
|
|
|
filename, NULL);
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
/*
|
|
|
|
* List the fingerprint of the OpenSSH-formatted key.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
|
|
|
|
check_fp(tmpfilename1, fp, "%s openssh encrypted fp", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the OpenSSH-formatted key in
|
|
|
|
* OpenSSH format.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-L", osfilename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the OpenSSH-formatted key in
|
|
|
|
* IETF/ssh.com format.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-p", osfilename, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Export the private key into ssh.com format, this time
|
|
|
|
* while encrypted. For RSA1 keys, this should give an
|
|
|
|
* error.
|
|
|
|
*/
|
|
|
|
if (i == 0)
|
|
|
|
setup_passphrases(NULL); /* error, hence no passphrase read */
|
|
|
|
else
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,
|
|
|
|
filename, NULL);
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
/*
|
|
|
|
* List the fingerprint of the ssh.com-formatted key.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
|
|
|
|
check_fp(tmpfilename1, fp, "%s ssh.com encrypted fp", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the ssh.com-formatted key in
|
|
|
|
* OpenSSH format.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-L", scfilename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the public half of the ssh.com-formatted key in
|
|
|
|
* IETF/ssh.com format.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-p", scfilename, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
/*
|
|
|
|
* Convert from OpenSSH into ssh.com.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", osfilename, "-o", tmpfilename1,
|
|
|
|
"-O", "private-sshcom", NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert from ssh.com back into a PuTTY key,
|
|
|
|
* supplying the same comment as we had before we
|
|
|
|
* started to ensure the comparison works.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
|
|
|
"-o", tmpfilename2, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if the PuTTY key thus generated is the same as
|
|
|
|
* the original.
|
|
|
|
*/
|
|
|
|
filecmp(filename, tmpfilename2,
|
|
|
|
"p->o->s->p encrypted %s", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert from ssh.com to OpenSSH.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", scfilename, "-o", tmpfilename1,
|
|
|
|
"-O", "private-openssh", NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert from OpenSSH back into a PuTTY key,
|
|
|
|
* supplying the same comment as we had before we
|
|
|
|
* started to ensure the comparison works.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
|
|
|
"-o", tmpfilename2, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if the PuTTY key thus generated is the same as
|
|
|
|
* the original.
|
|
|
|
*/
|
|
|
|
filecmp(filename, tmpfilename2,
|
|
|
|
"p->s->o->p encrypted %s", keytypes[i]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, do a round-trip conversion between PuTTY
|
|
|
|
* and ssh.com without involving OpenSSH, to test that
|
|
|
|
* the key comment is preserved in that case.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
|
|
|
|
filename, NULL);
|
|
|
|
setup_passphrases("sponge2", NULL);
|
|
|
|
test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
|
|
|
|
filecmp(filename, tmpfilename2,
|
|
|
|
"p->s->p encrypted %s", keytypes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load with the wrong passphrase.
|
|
|
|
*/
|
|
|
|
setup_passphrases("sponge8", NULL);
|
|
|
|
test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load a totally bogus file.
|
|
|
|
*/
|
|
|
|
setup_passphrases(NULL);
|
|
|
|
test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL);
|
|
|
|
}
|
|
|
|
printf("%d passes, %d fails\n", passes, fails);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|