OBJS1 = sshcrc.$(OBJ) sshdes.$(OBJ) sshmd5.$(OBJ) sshrsa.$(OBJ) sshrand.$(OBJ)
OBJS2 = sshsha.$(OBJ) sshblowf.$(OBJ) noise.$(OBJ) sshdh.$(OBJ) sshdss.$(OBJ)
OBJS3 = sshbn.$(OBJ) sshpubk.$(OBJ) ssh.$(OBJ) pageantc.$(OBJ) sshzlib.$(OBJ)
-OBJS4 = x11fwd.$(OBJ) portfwd.$(OBJ) sshaes.$(OBJ)
+OBJS4 = x11fwd.$(OBJ) portfwd.$(OBJ) sshaes.$(OBJ) sshsh512.$(OBJ)
##-- objects pageant
PAGE1 = pageant.$(OBJ) sshrsa.$(OBJ) sshpubk.$(OBJ) sshdes.$(OBJ) sshbn.$(OBJ)
PAGE2 = sshmd5.$(OBJ) version.$(OBJ) tree234.$(OBJ) misc.$(OBJ) sshaes.$(OBJ)
-PAGE3 = sshsha.$(OBJ) pageantc.$(OBJ)
+PAGE3 = sshsha.$(OBJ) pageantc.$(OBJ) sshdss.$(OBJ) sshsh512.$(OBJ)
##-- objects puttygen
-GEN1 = puttygen.$(OBJ) sshrsag.$(OBJ) sshprime.$(OBJ) sshdes.$(OBJ)
-GEN2 = sshbn.$(OBJ) sshmd5.$(OBJ) version.$(OBJ) sshrand.$(OBJ) noise.$(OBJ)
-GEN3 = sshsha.$(OBJ) winstore.$(OBJ) misc.$(OBJ) winctrls.$(OBJ)
-GEN4 = sshrsa.$(OBJ) sshpubk.$(OBJ) sshaes.$(OBJ)
+GEN1 = puttygen.$(OBJ) sshrsag.$(OBJ) sshdssg.$(OBJ) sshprime.$(OBJ)
+GEN2 = sshdes.$(OBJ) sshbn.$(OBJ) sshmd5.$(OBJ) version.$(OBJ) sshrand.$(OBJ)
+GEN3 = noise.$(OBJ) sshsha.$(OBJ) winstore.$(OBJ) misc.$(OBJ) winctrls.$(OBJ)
+GEN4 = sshrsa.$(OBJ) sshdss.$(OBJ) sshpubk.$(OBJ) sshaes.$(OBJ) sshsh512.$(OBJ)
##-- resources putty puttytel
PRESRC = win_res.$(RES)
##-- resources pageant
be_all.$(OBJ): be_all.c network.h misc.h puttymem.h putty.h
be_none.$(OBJ): be_none.c network.h misc.h puttymem.h putty.h
be_nossh.$(OBJ): be_nossh.c network.h misc.h puttymem.h putty.h
-ber.$(OBJ): ber.c network.h asn.h misc.h asnerror.h puttymem.h ssh.h putty.h
+ber.$(OBJ): ber.c network.h asn.h misc.h asnerror.h int64.h puttymem.h ssh.h putty.h
cert.$(OBJ): cert.c asn.h asnerror.h misc.h puttymem.h cert.h crypto.h
+debug.$(OBJ): debug.c debug.h
int64.$(OBJ): int64.c int64.h
ldisc.$(OBJ): ldisc.c network.h misc.h puttymem.h putty.h
misc.$(OBJ): misc.c network.h misc.h puttymem.h putty.h
-mscrypto.$(OBJ): mscrypto.c network.h puttymem.h ssh.h
+mscrypto.$(OBJ): mscrypto.c network.h int64.h puttymem.h ssh.h
no_ssl.$(OBJ): no_ssl.c network.h misc.h puttymem.h putty.h
-noise.$(OBJ): noise.c network.h misc.h puttymem.h storage.h ssh.h putty.h
-pageant.$(OBJ): pageant.c network.h puttymem.h ssh.h tree234.h
+noise.$(OBJ): noise.c network.h misc.h puttymem.h storage.h int64.h ssh.h putty.h
+pageant.$(OBJ): pageant.c network.h int64.h puttymem.h ssh.h tree234.h
pageantc.$(OBJ): pageantc.c puttymem.h
plink.$(OBJ): plink.c network.h misc.h puttymem.h storage.h putty.h tree234.h
-portfwd.$(OBJ): portfwd.c network.h misc.h puttymem.h ssh.h putty.h
+portfwd.$(OBJ): portfwd.c network.h misc.h int64.h puttymem.h ssh.h putty.h
psftp.$(OBJ): psftp.c network.h misc.h sftp.h ssh.h storage.h int64.h puttymem.h putty.h
-puttygen.$(OBJ): puttygen.c network.h misc.h puttymem.h winstuff.h ssh.h putty.h
+puttygen.$(OBJ): puttygen.c network.h misc.h int64.h puttymem.h winstuff.h ssh.h putty.h
raw.$(OBJ): raw.c network.h misc.h puttymem.h putty.h
rlogin.$(OBJ): rlogin.c network.h misc.h puttymem.h putty.h
scp.$(OBJ): scp.c network.h misc.h sftp.h ssh.h storage.h puttymem.h int64.h putty.h winstuff.h
settings.$(OBJ): settings.c network.h misc.h puttymem.h storage.h putty.h
sftp.$(OBJ): sftp.c sftp.h int64.h
sizetip.$(OBJ): sizetip.c network.h misc.h puttymem.h winstuff.h putty.h
-ssh.$(OBJ): ssh.c network.h misc.h puttymem.h ssh.h putty.h tree234.h
-sshaes.$(OBJ): sshaes.c network.h puttymem.h ssh.h
-sshblowf.$(OBJ): sshblowf.c network.h puttymem.h ssh.h
-sshbn.$(OBJ): sshbn.c network.h misc.h puttymem.h ssh.h putty.h
+ssh.$(OBJ): ssh.c network.h misc.h int64.h puttymem.h ssh.h putty.h tree234.h
+sshaes.$(OBJ): sshaes.c network.h int64.h puttymem.h ssh.h
+sshblowf.$(OBJ): sshblowf.c network.h int64.h puttymem.h ssh.h
+sshbn.$(OBJ): sshbn.c network.h misc.h int64.h puttymem.h ssh.h putty.h
sshcrc.$(OBJ): sshcrc.c
-sshdes.$(OBJ): sshdes.c network.h puttymem.h ssh.h
-sshdh.$(OBJ): sshdh.c network.h puttymem.h ssh.h
-sshdss.$(OBJ): sshdss.c network.h puttymem.h ssh.h
-sshmd5.$(OBJ): sshmd5.c network.h puttymem.h ssh.h
-sshprime.$(OBJ): sshprime.c network.h puttymem.h ssh.h
-sshpubk.$(OBJ): sshpubk.c network.h puttymem.h ssh.h
-sshrand.$(OBJ): sshrand.c network.h puttymem.h ssh.h
-sshrsa.$(OBJ): sshrsa.c network.h puttymem.h ssh.h
-sshrsag.$(OBJ): sshrsag.c network.h puttymem.h ssh.h
-sshsha.$(OBJ): sshsha.c network.h puttymem.h ssh.h
-sshzlib.$(OBJ): sshzlib.c network.h puttymem.h ssh.h
+sshdes.$(OBJ): sshdes.c network.h int64.h puttymem.h ssh.h
+sshdh.$(OBJ): sshdh.c network.h int64.h puttymem.h ssh.h
+sshdss.$(OBJ): sshdss.c network.h misc.h int64.h puttymem.h ssh.h
+sshdssg.$(OBJ): sshdssg.c network.h misc.h int64.h puttymem.h ssh.h
+sshmd5.$(OBJ): sshmd5.c network.h int64.h puttymem.h ssh.h
+sshprime.$(OBJ): sshprime.c network.h int64.h puttymem.h ssh.h
+sshpubk.$(OBJ): sshpubk.c network.h int64.h puttymem.h ssh.h
+sshrand.$(OBJ): sshrand.c network.h int64.h puttymem.h ssh.h
+sshrsa.$(OBJ): sshrsa.c network.h int64.h puttymem.h ssh.h
+sshrsag.$(OBJ): sshrsag.c network.h int64.h puttymem.h ssh.h
+sshsh512.$(OBJ): sshsh512.c network.h int64.h puttymem.h ssh.h
+sshsha.$(OBJ): sshsha.c network.h int64.h puttymem.h ssh.h
+sshzlib.$(OBJ): sshzlib.c network.h int64.h puttymem.h ssh.h
ssl.$(OBJ): ssl.c network.h asnerror.h misc.h cert.h crypto.h ssl.h int64.h puttymem.h
telnet.$(OBJ): telnet.c network.h misc.h puttymem.h putty.h
terminal.$(OBJ): terminal.c network.h misc.h puttymem.h putty.h tree234.h
-test.$(OBJ): test.c network.h puttymem.h ssh.h
+test.$(OBJ): test.c network.h int64.h puttymem.h ssh.h
tree234.$(OBJ): tree234.c tree234.h
unicode.$(OBJ): unicode.c network.h misc.h puttymem.h putty.h
version.$(OBJ): version.c
wcwidth.$(OBJ): wcwidth.c
wildcard.$(OBJ): wildcard.c
winctrls.$(OBJ): winctrls.c network.h misc.h puttymem.h putty.h winstuff.h
-windlg.$(OBJ): windlg.c network.h misc.h ssh.h storage.h puttymem.h putty.h winstuff.h win_res.h
+windlg.$(OBJ): windlg.c network.h misc.h ssh.h storage.h puttymem.h int64.h putty.h winstuff.h win_res.h
window.$(OBJ): window.c network.h misc.h puttymem.h storage.h winstuff.h putty.h win_res.h
winnet.$(OBJ): winnet.c network.h misc.h puttymem.h putty.h tree234.h
winstore.$(OBJ): winstore.c network.h misc.h puttymem.h storage.h putty.h
-x11fwd.$(OBJ): x11fwd.c network.h misc.h puttymem.h ssh.h putty.h
+x11fwd.$(OBJ): x11fwd.c network.h misc.h int64.h puttymem.h ssh.h putty.h
##--
# Hack to force version.obj to be rebuilt always
-\versionid $Id: pubkey.but,v 1.4 2001/09/22 15:36:44 simon Exp $
+\versionid $Id: pubkey.but,v 1.5 2001/09/22 20:52:21 simon Exp $
\# FIXME: passphrases, examples (e.g what does a key for pasting into
\# authorized_keys look like?), index entries, links.
disk. Many people feel this is a good compromise between security
and convenience. See \k{pageant} for further details.
-\H{pubkey-puttygen} PuTTYgen: RSA key generator for PuTTY
+\S{pubkey-types} Different types of public key
+
+The PuTTY key generator, described in \k{pubkey-puttygen}, offers
+you the opportunity to generate several types of key pair:
+
+\b An RSA key for use with the SSH 1 protocol.
+\b An RSA key for use with the SSH 2 protocol.
+\b A DSA key for use with the SSH 2 protocol.
+
+The SSH 1 protocol only supports RSA keys; if you will be connecting
+using the SSH 1 protocol, you must select the first key type or your
+key will be completely useless.
+
+SSH 2 supports more than one key type. The two types supported by
+PuTTY are RSA and DSA.
+
+The PuTTY developers \e{strongly} recommend you use RSA. DSA has an
+intrinsic weakness which makes it very easy to create a signature
+which contains enough information to give away the \e{private} key!
+This would allow an attacker to pretend to be you for any number of
+future sessions. PuTTY's implementation has taken very careful
+precautions to avoid this weakness, but we cannot be 100% certain we
+have managed it, and if you have the choice we strongly recommend
+using RSA keys instead.
+
+If you really need to connect to an SSH server which only supports
+DSA, then you probably have no choice but to use DSA. If you do use
+DSA, we recommend you do not use the same key to authenticate with
+more than one server.
+
+\H{pubkey-puttygen} PuTTYgen: Key generator for PuTTY
PuTTYgen is a key generator. It generates pairs of public and private
keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY
\S{pubkey-puttygen-generate} Generate a new key
Before generating a new key you have to choose the strength of the
-encryption. With \e{Parameters} you define the strength of the key. The
-default of 1024 should be OK for most users.
+encryption, and the type of the key (see \k{pubkey-types}). With
+\e{Parameters} you define the strength of the key. The default of
+1024 should be OK for most users.
Pressing the \e{Generate} button starts the process of generating a
new key pair. You then have to move the mouse over the blank area in
Connect to your SSH server using PuTTY with the SSH protocol. When the
connection succeeds you will be prompted for your user name and
-password to login. Once logged in change into the \c{.ssh} directory
-and open the file \c{authorized_keys} with your favorite editor (you
-may have to create this file if this is the first key to add).
-
-Switch to the PuTTYgen window and select all of the content below
-\e{Public key for pasting into authorized_keys file}, copy it to the
-clipboard (\c{Ctrl+C}). Then, switch back to the PuTTY window and
-insert the data into the open file. Save the file.
-
-From now on you can use the private key for authentication to this
-host. Either select the private key in PuTTY's \e{Connection},
-\e{SSH} panel: \e{Private key file for authentication} dialog or use
-it with Pageant as described in \k{pageant}.
+password to login. Once logged in, you must configure the server to
+accept your public key for authentication:
+
+\b If your server is using the SSH 1 protocol, you should change
+into the \c{.ssh} directory and open the file \c{authorized_keys}
+with your favorite editor. (You may have to create this file if this
+is the first key you have put in it). Then switch to the PuTTYgen
+window, select all of the text in the \e{Public key for pasting into
+authorized_keys file} box, and copy it to the clipboard
+(\c{Ctrl+C}). Then, switch back to the PuTTY window and insert the
+data into the open file, making sure it ends up all on one line.
+Save the file.
+
+\b If your server is OpenSSH and is using the SSH 2 protocol, you
+should follow the same instructions except that the file will be
+called \c{authorized_keys2}.
+
+\b If your server is \cw{ssh.com}'s SSH 2 product, you need to save
+a \e{public} key file from PuTTYgen, and copy that into the
+\c{.ssh2} directory on the server. Then you should go into that
+\c{.ssh2} directory, and edit (or create) a file called
+\c{authorization}. In this file you should put a line like \c{Key
+mykey.pub}, with \c{mykey.pub} replaced by the name of your key
+file.
+
+\b For other SSH server software, you should refer to the manual for
+that server.
+
+From now on you should be able to use the private key for
+authentication to this host. Either select the private key in
+PuTTY's \e{Connection}, \e{SSH} panel: \e{Private key file for
+authentication} dialog or use it with Pageant as described in
+\k{pageant}.
* pads its data with random bytes. Since we only use rsadecrypt()
* and the signing functions, which are deterministic, this should
* never be called.
- *
+ *
* If it _is_ called, there is a _serious_ problem, because it
* won't generate true random numbers. So we must scream, panic,
* and exit immediately if that should happen.
break;
case SSH2_AGENTC_SIGN_REQUEST:
/*
- * Reply with either SSH2_AGENT_RSA_RESPONSE or
+ * Reply with either SSH2_AGENT_SIGN_RESPONSE or
* SSH_AGENT_FAILURE, depending on whether we have that key
* or not.
*/
/* Add further algorithm names here. */
if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
key->alg = &ssh_rsa;
+ else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
+ key->alg = &ssh_dss;
else {
sfree(key);
goto failure;
/* ----------------------------------------------------------------------
* Progress report code. This is really horrible :-)
*/
-#define PHASE1TOTAL 0x10000
-#define PHASE2TOTAL 0x10000
-#define PHASE3TOTAL 0x04000
-#define PHASE1START 0
-#define PHASE2START (PHASE1TOTAL)
-#define PHASE3START (PHASE1TOTAL + PHASE2TOTAL)
-#define TOTALTOTAL (PHASE1TOTAL + PHASE2TOTAL + PHASE3TOTAL)
-#define PROGRESSBIGRANGE 65535
-#define DIVISOR ((TOTALTOTAL + PROGRESSBIGRANGE - 1) / PROGRESSBIGRANGE)
-#define PROGRESSRANGE (TOTALTOTAL / DIVISOR)
+#define PROGRESSRANGE 65535
+#define MAXPHASE 5
struct progress {
- unsigned phase1param, phase1current, phase1n;
- unsigned phase2param, phase2current, phase2n;
- unsigned phase3mult;
+ int nphases;
+ struct {
+ int exponential;
+ unsigned startpoint, total;
+ unsigned param, current, n; /* if exponential */
+ unsigned mult; /* if linear */
+ } phases[MAXPHASE];
+ unsigned total, divisor, range;
HWND progbar;
};
-static void progress_update(void *param, int phase, int iprogress)
+static void progress_update(void *param, int action, int phase, int iprogress)
{
struct progress *p = (struct progress *) param;
unsigned progress = iprogress;
int position;
- switch (phase) {
- case -1:
- p->phase1param = 0x10000 + progress;
- p->phase1current = 0x10000;
- p->phase1n = 0;
- return;
- case -2:
- p->phase2param = 0x10000 + progress;
- p->phase2current = 0x10000;
- p->phase2n = 0;
- return;
- case -3:
- p->phase3mult = PHASE3TOTAL / progress;
- return;
- case 1:
- while (p->phase1n < progress) {
- p->phase1n++;
- p->phase1current *= p->phase1param;
- p->phase1current /= 0x10000;
- }
- position = PHASE1START + 0x10000 - p->phase1current;
+ if (action < PROGFN_READY && p->nphases < phase)
+ p->nphases = phase;
+ switch (action) {
+ case PROGFN_LIN_PHASE:
+ p->phases[phase-1].exponential = 0;
+ p->phases[phase-1].mult = p->phases[phase].total / progress;
+ break;
+ case PROGFN_EXP_PHASE:
+ p->phases[phase-1].exponential = 1;
+ p->phases[phase-1].param = 0x10000 + progress;
+ p->phases[phase-1].current = p->phases[phase-1].total;
+ p->phases[phase-1].n = 0;
break;
- case 2:
- while (p->phase2n < progress) {
- p->phase2n++;
- p->phase2current *= p->phase2param;
- p->phase2current /= 0x10000;
+ case PROGFN_PHASE_EXTENT:
+ p->phases[phase-1].total = progress;
+ break;
+ case PROGFN_READY:
+ {
+ unsigned total = 0;
+ int i;
+ for (i = 0; i < p->nphases; i++) {
+ p->phases[i].startpoint = total;
+ total += p->phases[i].total;
+ }
+ p->total = total;
+ p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
+ p->range = p->total / p->divisor;
+ SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
}
- position = PHASE2START + 0x10000 - p->phase2current;
break;
- case 3:
- position = PHASE3START + progress * p->phase3mult;
+ case PROGFN_PROGRESS:
+ if (p->phases[phase-1].exponential) {
+ while (p->phases[phase-1].n < progress) {
+ p->phases[phase-1].n++;
+ p->phases[phase-1].current *= p->phases[phase-1].param;
+ p->phases[phase-1].current /= 0x10000;
+ }
+ position = (p->phases[phase-1].startpoint +
+ p->phases[phase-1].total - p->phases[phase-1].current);
+ } else {
+ position = (p->phases[phase-1].startpoint +
+ progress * p->phases[phase-1].mult);
+ }
+ SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
break;
- default:
- /*
- * Shouldn't happen, but having a default clause placates
- * gcc -Wall, which would otherwise complain that
- * `position' might be used uninitialised.
- */
- return;
}
-
- SendMessage(p->progbar, PBM_SETPOS, position / DIVISOR, 0);
}
extern char ver[];
HWND progressbar; /* notify this with progress */
HWND dialog; /* notify this on completion */
int keysize; /* bits in key */
+ int is_dsa;
struct RSAKey *key;
+ struct dss_key *dsskey;
};
static DWORD WINAPI generate_rsa_key_thread(void *param)
{
struct progress prog;
prog.progbar = params->progressbar;
- rsa_generate(params->key, params->keysize, progress_update, &prog);
+ if (params->is_dsa)
+ dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
+ else
+ rsa_generate(params->key, params->keysize, progress_update, &prog);
PostMessage(params->dialog, WM_DONEKEY, 0, 0);
int key_exists;
int entropy_got, entropy_required, entropy_size;
int keysize;
- int ssh2;
+ int ssh2, is_dsa;
char **commentptr; /* points to key.comment or ssh2key.comment */
struct ssh2_userkey ssh2key;
unsigned *entropy;
struct RSAKey key;
+ struct dss_key dsskey;
};
static void hidemany(HWND hwnd, const int *ids, int hideit)
IDC_LOADSTATIC, IDC_LOAD,
IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
IDC_BOX_PARAMS,
- IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA,
+ IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
IDC_BITSSTATIC, IDC_BITS,
IDC_ABOUT,
};
{
struct ctlpos cp, cp2;
- /* Accelerators used: acglops1rb */
+ /* Accelerators used: acglops1rbd */
ctlposinit(&cp, hwnd, 10, 10, 10);
bartitle(&cp, "Public and private key generation for PuTTY",
"&Save private key", IDC_SAVE);
endbox(&cp);
beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
- radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 2,
+ radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
"SSH&1 (RSA)", IDC_KEYSSH1,
- "SSH2 &RSA", IDC_KEYSSH2RSA, NULL);
+ "SSH2 &RSA", IDC_KEYSSH2RSA,
+ "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
staticedit(&cp, "Number of &bits in a generated key:",
IDC_BITSSTATIC, IDC_BITS, 20);
endbox(&cp);
params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
params->dialog = hwnd;
params->keysize = state->keysize;
+ params->is_dsa = state->is_dsa;
params->key = &state->key;
+ params->dsskey = &state->dsskey;
if (!CreateThread(NULL, 0, generate_rsa_key_thread,
params, 0, &threadid)) {
state->keysize = DEFAULT_KEYSIZE;
/* If we ever introduce a new key type, check it here! */
state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
+ state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
if (state->keysize < 256) {
int ret = MessageBox(hwnd,
"PuTTYgen will not generate a key"
state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
state->generation_thread_exists = FALSE;
state->key_exists = TRUE;
- SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE,
- 0);
+ SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
+ MAKELPARAM(0, PROGRESSRANGE));
+ SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
if (state->ssh2) {
- state->ssh2key.data = &state->key;
- state->ssh2key.alg = &ssh_rsa;
+ if (state->is_dsa) {
+ state->ssh2key.data = &state->dsskey;
+ state->ssh2key.alg = &ssh_dss;
+ } else {
+ state->ssh2key.data = &state->key;
+ state->ssh2key.alg = &ssh_rsa;
+ }
state->commentptr = &state->ssh2key.comment;
} else {
state->commentptr = &state->key.comment;
struct tm *tm;
time(&t);
tm = localtime(&t);
- strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
+ if (state->is_dsa)
+ strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
+ else
+ strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
}
/*
#include "puttymem.h"
#include "network.h"
+#include "int64.h"
struct ssh_channel;
char *comment;
};
+struct dss_key {
+ Bignum p, q, g, y, x;
+};
+
int makekey(unsigned char *data, struct RSAKey *result,
unsigned char **keystr, int order);
int makeprivate(unsigned char *data, struct RSAKey *result);
int blkused;
uint32 lenhi, lenlo;
} SHA_State;
-
void SHA_Init(SHA_State * s);
void SHA_Bytes(SHA_State * s, void *p, int len);
void SHA_Final(SHA_State * s, unsigned char *output);
void SHA_Simple(void *p, int len, unsigned char *output);
+void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
+ unsigned char *output);
+
+typedef struct {
+ uint64 h[8];
+ unsigned char block[128];
+ int blkused;
+ uint32 len[4];
+} SHA512_State;
+void SHA512_Init(SHA512_State * s);
+void SHA512_Bytes(SHA512_State * s, const void *p, int len);
+void SHA512_Final(SHA512_State * s, unsigned char *output);
+void SHA512_Simple(const void *p, int len, unsigned char *output);
+
struct ssh_cipher {
void (*sesskey) (unsigned char *key); /* for ssh 1 */
void (*encrypt) (unsigned char *blk, int len);
Bignum copybn(Bignum b);
Bignum bn_power_2(int n);
void bn_restore_invariant(Bignum b);
-Bignum bignum_from_short(unsigned short n);
+Bignum bignum_from_long(unsigned long n);
void freebn(Bignum b);
Bignum modpow(Bignum base, Bignum exp, Bignum mod);
Bignum modmul(Bignum a, Bignum b, Bignum mod);
unsigned short bignum_mod_short(Bignum number, unsigned short modulus);
Bignum bignum_add_long(Bignum number, unsigned long addend);
Bignum bigmul(Bignum a, Bignum b);
+Bignum bigmuladd(Bignum a, Bignum b, Bignum addend);
+Bignum bigdiv(Bignum a, Bignum b);
+Bignum bigmod(Bignum a, Bignum b);
Bignum modinv(Bignum number, Bignum modulus);
Bignum bignum_bitmask(Bignum number);
Bignum bignum_rshift(Bignum number, int shift);
/*
* For progress updates in the key generation utility.
*/
-typedef void (*progfn_t) (void *param, int phase, int progress);
+#define PROGFN_LIN_PHASE 1
+#define PROGFN_EXP_PHASE 2
+#define PROGFN_PHASE_EXTENT 3
+#define PROGFN_READY 4
+#define PROGFN_PROGRESS 5
+typedef void (*progfn_t) (void *param, int action, int phase, int progress);
int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
void *pfnparam);
-Bignum primegen(int bits, int modulus, int residue, int phase,
- progfn_t pfn, void *pfnparam);
+int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
+ void *pfnparam);
+Bignum primegen(int bits, int modulus, int residue, Bignum factor,
+ int phase, progfn_t pfn, void *pfnparam);
/*
#include <stdlib.h>
#include <string.h>
-#if 0 // use PuTTY main debugging for diagbn()
-#include <windows.h>
-#include "putty.h"
-#define debugprint debug
-#else
-#define debugprint(x) printf x
-#endif
+#include "misc.h"
#define BIGNUM_INTERNAL
typedef unsigned short *Bignum;
* Compute p % mod.
* The most significant word of mod MUST be non-zero.
* We assume that the result array is the same size as the mod array.
- * We optionally write out a quotient.
+ * We optionally write out a quotient if `quotient' is non-NULL.
+ * We can avoid writing out the result if `result' is NULL.
*/
-void bigmod(Bignum p, Bignum mod, Bignum result, Bignum quotient)
+void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient)
{
unsigned short *n, *m;
int mshift;
}
/* Copy result to buffer */
- for (i = 1; i <= result[0]; i++) {
- int j = plen - i;
- result[i] = j >= 0 ? n[j] : 0;
+ if (result) {
+ for (i = 1; i <= result[0]; i++) {
+ int j = plen - i;
+ result[i] = j >= 0 ? n[j] : 0;
+ }
}
/* Free temporary arrays */
}
/*
- * Convert a (max 16-bit) short into a bignum.
+ * Convert a (max 32-bit) long into a bignum.
*/
-Bignum bignum_from_short(unsigned short n)
+Bignum bignum_from_long(unsigned long n)
{
Bignum ret;
- ret = newbn(2);
+ ret = newbn(3);
ret[1] = n & 0xFFFF;
ret[2] = (n >> 16) & 0xFFFF;
- ret[0] = (ret[2] ? 2 : 1);
+ ret[3] = 0;
+ ret[0] = (ret[2] ? 2 : 1);
return ret;
}
int i, nibbles, morenibbles;
static const char hex[] = "0123456789ABCDEF";
- debugprint(("%s0x", prefix ? prefix : ""));
+ debug(("%s0x", prefix ? prefix : ""));
nibbles = (3 + bignum_bitcount(md)) / 4;
if (nibbles < 1)
nibbles = 1;
morenibbles = 4 * md[0] - nibbles;
for (i = 0; i < morenibbles; i++)
- debugprint(("-"));
+ debug(("-"));
for (i = nibbles; i--;)
- debugprint(
- ("%c",
- hex[(bignum_byte(md, i / 2) >> (4 * (i % 2))) & 0xF]));
+ debug(("%c",
+ hex[(bignum_byte(md, i / 2) >> (4 * (i % 2))) & 0xF]));
if (prefix)
- debugprint(("\n"));
+ debug(("\n"));
+}
+
+/*
+ * Simple division.
+ */
+Bignum bigdiv(Bignum a, Bignum b)
+{
+ Bignum q = newbn(a[0]);
+ bigdivmod(a, b, NULL, q);
+ return q;
+}
+
+/*
+ * Simple remainder.
+ */
+Bignum bigmod(Bignum a, Bignum b)
+{
+ Bignum r = newbn(b[0]);
+ bigdivmod(a, b, r, NULL);
+ return r;
}
/*
Bignum a = copybn(av);
Bignum b = copybn(bv);
- diagbn("a = ", a);
- diagbn("b = ", b);
while (bignum_cmp(b, Zero) != 0) {
Bignum t = newbn(b[0]);
- bigmod(a, b, t, NULL);
- diagbn("t = ", t);
+ bigdivmod(a, b, t, NULL);
while (t[0] > 1 && t[t[0]] == 0)
t[0]--;
freebn(a);
while (bignum_cmp(b, One) != 0) {
Bignum t = newbn(b[0]);
Bignum q = newbn(a[0]);
- bigmod(a, b, t, q);
+ bigdivmod(a, b, t, q);
while (t[0] > 1 && t[t[0]] == 0)
t[0]--;
freebn(a);
#include <assert.h>
#include "ssh.h"
+#include "misc.h"
#define GET_32BIT(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
(cp)[2] = (unsigned char)((value) >> 8); \
(cp)[3] = (unsigned char)(value); }
-#if 0
-#define DEBUG_DSS
-#else
-#define diagbn(x,y)
-#endif
+static void sha_mpint(SHA_State * s, Bignum b)
+{
+ unsigned char *p;
+ unsigned char lenbuf[4];
+ int len;
+ len = (bignum_bitcount(b) + 8) / 8;
+ PUT_32BIT(lenbuf, len);
+ SHA_Bytes(s, lenbuf, 4);
+ while (len-- > 0) {
+ lenbuf[0] = bignum_byte(b, len);
+ SHA_Bytes(s, lenbuf, 1);
+ }
+ memset(lenbuf, 0, sizeof(lenbuf));
+}
+
+static void sha512_mpint(SHA512_State * s, Bignum b)
+{
+ unsigned char *p;
+ unsigned char lenbuf[4];
+ int len;
+ len = (bignum_bitcount(b) + 8) / 8;
+ PUT_32BIT(lenbuf, len);
+ SHA512_Bytes(s, lenbuf, 4);
+ while (len-- > 0) {
+ lenbuf[0] = bignum_byte(b, len);
+ SHA512_Bytes(s, lenbuf, 1);
+ }
+ memset(lenbuf, 0, sizeof(lenbuf));
+}
static void getstring(char **data, int *datalen, char **p, int *length)
{
return b;
}
-struct dss_key {
- Bignum p, q, g, y;
-};
-
static void *dss_newkey(char *data, int len)
{
char *p;
}
sig += 4, siglen -= 4; /* skip yet another length field */
}
- diagbn("p=", dss->p);
- diagbn("q=", dss->q);
- diagbn("g=", dss->g);
- diagbn("y=", dss->y);
r = get160(&sig, &siglen);
- diagbn("r=", r);
s = get160(&sig, &siglen);
- diagbn("s=", s);
if (!r || !s)
return 0;
* Step 1. w <- s^-1 mod q.
*/
w = modinv(s, dss->q);
- diagbn("w=", w);
/*
* Step 2. u1 <- SHA(message) * w mod q.
p = hash;
slen = 20;
sha = get160(&p, &slen);
- diagbn("sha=", sha);
u1 = modmul(sha, w, dss->q);
- diagbn("u1=", u1);
/*
* Step 3. u2 <- r * w mod q.
*/
u2 = modmul(r, w, dss->q);
- diagbn("u2=", u2);
/*
* Step 4. v <- (g^u1 * y^u2 mod p) mod q.
*/
gu1p = modpow(dss->g, u1, dss->p);
- diagbn("gu1p=", gu1p);
yu2p = modpow(dss->y, u2, dss->p);
- diagbn("yu2p=", yu2p);
gu1yu2p = modmul(gu1p, yu2p, dss->p);
- diagbn("gu1yu2p=", gu1yu2p);
v = modmul(gu1yu2p, One, dss->q);
- diagbn("gu1yu2q=v=", v);
- diagbn("r=", r);
/*
* Step 5. v should now be equal to r.
static unsigned char *dss_private_blob(void *key, int *len)
{
- return NULL; /* can't handle DSS private keys */
+ struct dss_key *dss = (struct dss_key *) key;
+ int xlen, bloblen;
+ int i;
+ unsigned char *blob, *p;
+ SHA_State s;
+ unsigned char digest[20];
+
+ xlen = (bignum_bitcount(dss->x) + 8) / 8;
+
+ /*
+ * mpint x, string[20] the SHA of p||q||g. Total 28 + xlen.
+ * (two length fields and twenty bytes, 20+8=28).
+ */
+ bloblen = 28 + xlen;
+ blob = smalloc(bloblen);
+ p = blob;
+ PUT_32BIT(p, xlen);
+ p += 4;
+ for (i = xlen; i--;)
+ *p++ = bignum_byte(dss->x, i);
+ PUT_32BIT(p, 20);
+ SHA_Init(&s);
+ sha_mpint(&s, dss->p);
+ sha_mpint(&s, dss->q);
+ sha_mpint(&s, dss->g);
+ SHA_Final(&s, digest);
+ p += 4;
+ for (i = 0; i < 20; i++)
+ *p++ = digest[i];
+ assert(p == blob + bloblen);
+ *len = bloblen;
+ return blob;
}
static void *dss_createkey(unsigned char *pub_blob, int pub_len,
unsigned char *priv_blob, int priv_len)
{
- return NULL; /* can't handle DSS private keys */
+ struct dss_key *dss;
+ char *pb = (char *) priv_blob;
+ char *hash;
+ int hashlen;
+ SHA_State s;
+ unsigned char digest[20];
+ Bignum ytest;
+
+ dss = dss_newkey((char *) pub_blob, pub_len);
+ dss->x = getmp(&pb, &priv_len);
+ getstring(&pb, &priv_len, &hash, &hashlen);
+
+ /*
+ * Verify details of the key. First check that the hash is
+ * indeed a hash of p||q||g.
+ */
+ if (hashlen != 20) {
+ dss_freekey(dss);
+ return NULL;
+ }
+ SHA_Init(&s);
+ sha_mpint(&s, dss->p);
+ sha_mpint(&s, dss->q);
+ sha_mpint(&s, dss->g);
+ SHA_Final(&s, digest);
+ if (0 != memcmp(hash, digest, 20)) {
+ dss_freekey(dss);
+ return NULL;
+ }
+
+ /*
+ * Now ensure g^x mod p really is y.
+ */
+ ytest = modpow(dss->g, dss->x, dss->p);
+ if (0 != bignum_cmp(ytest, dss->y)) {
+ dss_freekey(dss);
+ return NULL;
+ }
+ freebn(ytest);
+
+ return dss;
}
static void *dss_openssh_createkey(unsigned char **blob, int *len)
{
- return NULL; /* can't handle DSS private keys */
+ char **b = (char **) blob;
+ struct dss_key *dss;
+
+ dss = smalloc(sizeof(struct dss_key));
+ if (!dss)
+ return NULL;
+
+ dss->p = getmp(b, len);
+ dss->q = getmp(b, len);
+ dss->g = getmp(b, len);
+ dss->y = getmp(b, len);
+ dss->x = getmp(b, len);
+
+ if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x) {
+ sfree(dss->p);
+ sfree(dss->q);
+ sfree(dss->g);
+ sfree(dss->y);
+ sfree(dss->x);
+ sfree(dss);
+ return NULL;
+ }
+
+ return dss;
}
static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len)
{
- return -1; /* can't handle DSS private keys */
+ struct dss_key *dss = (struct dss_key *) key;
+ int bloblen, i;
+
+ bloblen =
+ ssh2_bignum_length(dss->p) +
+ ssh2_bignum_length(dss->q) +
+ ssh2_bignum_length(dss->g) +
+ ssh2_bignum_length(dss->y) +
+ ssh2_bignum_length(dss->x);
+
+ if (bloblen > len)
+ return bloblen;
+
+ bloblen = 0;
+#define ENC(x) \
+ PUT_32BIT(blob+bloblen, ssh2_bignum_length((x))-4); bloblen += 4; \
+ for (i = ssh2_bignum_length((x))-4; i-- ;) blob[bloblen++]=bignum_byte((x),i);
+ ENC(dss->p);
+ ENC(dss->q);
+ ENC(dss->g);
+ ENC(dss->y);
+ ENC(dss->x);
+
+ return bloblen;
}
unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
{
- return NULL; /* can't handle DSS private keys */
+ /*
+ * The basic DSS signing algorithm is:
+ *
+ * - invent a random k between 1 and q-1 (exclusive).
+ * - Compute r = (g^k mod p) mod q.
+ * - Compute s = k^-1 * (hash + x*r) mod q.
+ *
+ * This has the dangerous properties that:
+ *
+ * - if an attacker in possession of the public key _and_ the
+ * signature (for example, the host you just authenticated
+ * to) can guess your k, he can reverse the computation of s
+ * and work out x = r^-1 * (s*k - hash) mod q. That is, he
+ * can deduce the private half of your key, and masquerade
+ * as you for as long as the key is still valid.
+ *
+ * - since r is a function purely of k and the public key, if
+ * the attacker only has a _range of possibilities_ for k
+ * it's easy for him to work through them all and check each
+ * one against r; he'll never be unsure of whether he's got
+ * the right one.
+ *
+ * - if you ever sign two different hashes with the same k, it
+ * will be immediately obvious because the two signatures
+ * will have the same r, and moreover an attacker in
+ * possession of both signatures (and the public key of
+ * course) can compute k = (hash1-hash2) * (s1-s2)^-1 mod q,
+ * and from there deduce x as before.
+ *
+ * - the Bleichenbacher attack on DSA makes use of methods of
+ * generating k which are significantly non-uniformly
+ * distributed; in particular, generating a 160-bit random
+ * number and reducing it mod q is right out.
+ *
+ * For this reason we must be pretty careful about how we
+ * generate our k. Since this code runs on Windows, with no
+ * particularly good system entropy sources, we can't trust our
+ * RNG itself to produce properly unpredictable data. Hence, we
+ * use a totally different scheme instead.
+ *
+ * What we do is to take a SHA-512 (_big_) hash of the private
+ * key x, and then feed this into another SHA-512 hash that
+ * also includes the message hash being signed. That is:
+ *
+ * proto_k = SHA512 ( SHA512(x) || SHA160(message) )
+ *
+ * This number is 512 bits long, so reducing it mod q won't be
+ * noticeably non-uniform. So
+ *
+ * k = proto_k mod q
+ *
+ * This has the interesting property that it's _deterministic_:
+ * signing the same hash twice with the same key yields the
+ * same signature.
+ *
+ * (It doesn't, _per se_, protect against reuse of k. Reuse of
+ * k is left to chance; all it does is prevent _excessively
+ * high_ chances of reuse of k due to entropy problems.)
+ *
+ * Thanks to Colin Plumb for the general idea of using x to
+ * ensure k is hard to guess, and to the Cambridge University
+ * Computer Security Group for helping to argue out all the
+ * fine details.
+ */
+ struct dss_key *dss = (struct dss_key *) key;
+ SHA512_State ss;
+ unsigned char digest[20], digest512[64];
+ Bignum proto_k, k, gkp, hash, kinv, hxr, r, s;
+ unsigned char *bytes;
+ int nbytes, i;
+
+ SHA_Simple(data, datalen, digest);
+
+ /*
+ * Hash some identifying text plus x.
+ */
+ SHA512_Init(&ss);
+ SHA512_Bytes(&ss, "DSA deterministic k generator", 30);
+ sha512_mpint(&ss, dss->x);
+ SHA512_Final(&ss, digest512);
+
+ /*
+ * Now hash that digest plus the message hash.
+ */
+ SHA512_Init(&ss);
+ SHA512_Bytes(&ss, digest512, sizeof(digest512));
+ SHA512_Bytes(&ss, digest, sizeof(digest));
+ SHA512_Final(&ss, digest512);
+
+ memset(&ss, 0, sizeof(ss));
+
+ /*
+ * Now convert the result into a bignum, and reduce it mod q.
+ */
+ proto_k = bignum_from_bytes(digest512, 64);
+ k = bigmod(proto_k, dss->q);
+ freebn(proto_k);
+
+ memset(digest512, 0, sizeof(digest512));
+
+ /*
+ * Now we have k, so just go ahead and compute the signature.
+ */
+ gkp = modpow(dss->g, k, dss->p); /* g^k mod p */
+ r = bigmod(gkp, dss->q); /* r = (g^k mod p) mod q */
+ freebn(gkp);
+
+ hash = bignum_from_bytes(digest, 20);
+ kinv = modinv(k, dss->q); /* k^-1 mod q */
+ hxr = bigmuladd(dss->x, r, hash); /* hash + x*r */
+ s = modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash + x*r) mod q */
+ freebn(hxr);
+ freebn(kinv);
+ freebn(hash);
+
+ /*
+ * Signature blob is
+ *
+ * string "ssh-dss"
+ * string two 20-byte numbers r and s, end to end
+ *
+ * i.e. 4+7 + 4+40 bytes.
+ */
+ nbytes = 4 + 7 + 4 + 40;
+ bytes = smalloc(nbytes);
+ PUT_32BIT(bytes, 7);
+ memcpy(bytes + 4, "ssh-dss", 7);
+ PUT_32BIT(bytes + 4 + 7, 40);
+ for (i = 0; i < 20; i++) {
+ bytes[4 + 7 + 4 + i] = bignum_byte(r, 19 - i);
+ bytes[4 + 7 + 4 + 20 + i] = bignum_byte(s, 19 - i);
+ }
+ freebn(r);
+ freebn(s);
+
+ *siglen = nbytes;
+ return bytes;
}
const struct ssh_signkey ssh_dss = {
* Prime generation.
*/
+#include <assert.h>
#include "ssh.h"
/*
#define NPRIMES (sizeof(primes) / sizeof(*primes))
/*
- * Generate a prime. We arrange to select a prime with the property
- * (prime % modulus) != residue (to speed up use in RSA).
+ * Generate a prime. We can deal with various extra properties of
+ * the prime:
+ *
+ * - to speed up use in RSA, we can arrange to select a prime with
+ * the property (prime % modulus) != residue.
+ *
+ * - for use in DSA, we can arrange to select a prime which is one
+ * more than a multiple of a dirty great bignum. In this case
+ * `bits' gives the size of the factor by which we _multiply_
+ * that bignum, rather than the size of the whole number.
*/
-Bignum primegen(int bits, int modulus, int residue,
+Bignum primegen(int bits, int modulus, int residue, Bignum factor,
int phase, progfn_t pfn, void *pfnparam)
{
int i, k, v, byte, bitsleft, check, checks;
- unsigned long delta, moduli[NPRIMES + 1], residues[NPRIMES + 1];
+ unsigned long delta;
+ unsigned long moduli[NPRIMES + 1];
+ unsigned long residues[NPRIMES + 1];
+ unsigned long multipliers[NPRIMES + 1];
Bignum p, pm1, q, wqp, wqp2;
int progress = 0;
STARTOVER:
- pfn(pfnparam, phase, ++progress);
+ pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
/*
* Generate a k-bit random number with top and bottom bits set.
+ * Alternatively, if `factor' is nonzero, generate a k-bit
+ * random number with the top bit set and the bottom bit clear,
+ * multiply it by `factor', and add one.
*/
p = bn_power_2(bits - 1);
for (i = 0; i < bits; i++) {
if (i == 0 || i == bits - 1)
- v = 1;
+ v = (i != 0 || !factor) ? 1 : 0;
else {
if (bitsleft <= 0)
bitsleft = 8, byte = random_byte();
}
bignum_set_bit(p, i, v);
}
+ if (factor) {
+ Bignum tmp = p;
+ p = bigmul(tmp, factor);
+ freebn(tmp);
+ assert(bignum_bit(p, 0) == 0);
+ bignum_set_bit(p, 0, 1);
+ }
/*
* Ensure this random number is coprime to the first few
- * primes, by repeatedly adding 2 to it until it is.
+ * primes, by repeatedly adding either 2 or 2*factor to it
+ * until it is.
*/
for (i = 0; i < NPRIMES; i++) {
moduli[i] = primes[i];
residues[i] = bignum_mod_short(p, primes[i]);
+ if (factor)
+ multipliers[i] = bignum_mod_short(factor, primes[i]);
+ else
+ multipliers[i] = 1;
}
moduli[NPRIMES] = modulus;
residues[NPRIMES] = (bignum_mod_short(p, (unsigned short) modulus)
delta = 0;
while (1) {
for (i = 0; i < (sizeof(moduli) / sizeof(*moduli)); i++)
- if (!((residues[i] + delta) % moduli[i]))
+ if (!((residues[i] + delta * multipliers[i]) % moduli[i]))
break;
if (i < (sizeof(moduli) / sizeof(*moduli))) { /* we broke */
delta += 2;
- if (delta < 2) {
+ if (delta > 65536) {
freebn(p);
goto STARTOVER;
}
break;
}
q = p;
- p = bignum_add_long(q, delta);
+ if (factor) {
+ Bignum tmp;
+ tmp = bignum_from_long(delta);
+ p = bigmuladd(tmp, factor, q);
+ freebn(tmp);
+ } else {
+ p = bignum_add_long(q, delta);
+ }
freebn(q);
/*
break;
}
- pfn(pfnparam, phase, ++progress);
+ pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
/*
* Compute w^q mod p.
/*
* PuTTY's own format for SSH2 keys is as follows:
- *
+ *
* The file is text. Lines are terminated by CRLF, although CR-only
* and LF-only are tolerated on input.
- *
+ *
* The first line says "PuTTY-User-Key-File-1: " plus the name of the
- * algorithm ("ssh-dss", "ssh-rsa" etc. Although, of course, this
- * being PuTTY, "ssh-dss" is not supported.)
- *
+ * algorithm ("ssh-dss", "ssh-rsa" etc).
+ *
* The next line says "Encryption: " plus an encryption type.
* Currently the only supported encryption types are "aes256-cbc"
* and "none".
- *
+ *
* The next line says "Comment: " plus the comment string.
- *
+ *
* Next there is a line saying "Public-Lines: " plus a number N.
* The following N lines contain a base64 encoding of the public
* part of the key. This is encoded as the standard SSH2 public key
* blob (with no initial length): so for RSA, for example, it will
* read
- *
+ *
* string "ssh-rsa"
* mpint exponent
* mpint modulus
- *
+ *
* Next, there is a line saying "Private-Lines: " plus a number N,
* and then N lines containing the (potentially encrypted) private
* part of the key. For the key type "ssh-rsa", this will be
* composed of
- *
+ *
* mpint private_exponent
* mpint p (the larger of the two primes)
* mpint q (the smaller prime)
* mpint iqmp (the inverse of q modulo p)
* data padding (to reach a multiple of the cipher block size)
+ *
+ * And for "ssh-dss", it will be composed of
+ *
+ * mpint x (the private key parameter)
+ * string hash (20-byte hash of mpints p || q || g)
+ *
+ * Finally, there is a line saying _either_
+ *
+ * - "Private-Hash: " plus a hex representation of a SHA-1 hash of
+ * the plaintext version of the private part, including the
+ * final padding.
+ *
+ * or
+ *
+ * - "Private-MAC: " plus a hex representation of a HMAC-SHA-1 of
+ * the plaintext version of the private part, including the
+ * final padding.
*
- * Finally, there is a line saying "Private-Hash: " plus a hex
- * representation of a SHA-1 hash of the plaintext version of the
- * private part, including the final padding.
+ * The key to the MAC is itself a SHA-1 hash of:
*
+ * data "putty-private-key-file-mac-key"
+ * data passphrase
+ *
+ * Encrypted keys should have a MAC, whereas unencrypted ones must
+ * have a hash.
+ *
* If the key is encrypted, the encryption key is derived from the
* passphrase by means of a succession of SHA-1 hashes. Each hash
* is the hash of:
- *
+ *
* uint32 sequence-number
- * string passphrase
- *
+ * data passphrase
+ *
* where the sequence-number increases from zero. As many of these
* hashes are used as necessary.
- *
+ *
* NOTE! It is important that all _public_ data can be verified
* with reference to the _private_ data. There exist attacks based
* on modifying the public key but leaving the private section
* intact.
- *
+ *
* With RSA, this is easy: verify that n = p*q, and also verify
- * that e*d == 1 modulo (p-1)(q-1). With DSA (if we were ever to
- * support it), we would need to store extra data in the private
- * section other than just x.
+ * that e*d == 1 modulo (p-1)(q-1). With DSA, we need to store
+ * extra data in the private section other than just x, namely a
+ * hash of p||q||g. (It's then easy to verify that y is equal to
+ * g^x mod p.)
*/
static int read_header(FILE * fp, char *header)
struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
{
FILE *fp;
- char header[40], *b, *comment, *hash;
+ char header[40], *b, *comment, *mac;
const struct ssh_signkey *alg;
struct ssh2_userkey *ret;
int cipher, cipherblk;
unsigned char *public_blob, *private_blob;
int public_blob_len, private_blob_len;
- int i;
+ int i, is_mac;
+ int passlen = passphrase ? strlen(passphrase) : 0;
ret = NULL; /* return NULL for most errors */
- comment = hash = NULL;
+ comment = mac = NULL;
public_blob = private_blob = NULL;
fp = fopen(filename, "rb");
goto error;
if ((b = read_body(fp)) == NULL)
goto error;
- /* Select key algorithm structure. Currently only ssh-rsa. */
+ /* Select key algorithm structure. */
if (!strcmp(b, "ssh-rsa"))
alg = &ssh_rsa;
+ else if (!strcmp(b, "ssh-dss"))
+ alg = &ssh_dss;
else {
sfree(b);
goto error;
if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)
goto error;
- /* Read the Private-Hash header line. */
- if (!read_header(fp, header) || 0 != strcmp(header, "Private-Hash"))
+ /* Read the Private-MAC or Private-Hash header line. */
+ if (!read_header(fp, header))
goto error;
- if ((hash = read_body(fp)) == NULL)
+ if (0 == strcmp(header, "Private-MAC")) {
+ if ((mac = read_body(fp)) == NULL)
+ goto error;
+ is_mac = 1;
+ } else if (0 == strcmp(header, "Private-Hash")) {
+ if ((mac = read_body(fp)) == NULL)
+ goto error;
+ is_mac = 0;
+ } else
goto error;
fclose(fp);
if (cipher) {
unsigned char key[40];
SHA_State s;
- int passlen;
if (!passphrase)
goto error;
if (private_blob_len % cipherblk)
goto error;
- passlen = strlen(passphrase);
-
SHA_Init(&s);
SHA_Bytes(&s, "\0\0\0\0", 4);
SHA_Bytes(&s, passphrase, passlen);
* Verify the private hash.
*/
{
- char realhash[41];
+ char realmac[41];
unsigned char binary[20];
- SHA_Simple(private_blob, private_blob_len, binary);
+ if (is_mac) {
+ SHA_State s;
+ unsigned char mackey[20];
+ char header[] = "putty-private-key-file-mac-key";
+
+ if (!passphrase) /* can't have MAC in unencrypted key */
+ goto error;
+
+ SHA_Init(&s);
+ SHA_Bytes(&s, header, sizeof(header)-1);
+ SHA_Bytes(&s, passphrase, passlen);
+ SHA_Final(&s, mackey);
+
+ hmac_sha1_simple(mackey, 20, private_blob, private_blob_len,
+ binary);
+
+ memset(mackey, 0, sizeof(mackey));
+ memset(&s, 0, sizeof(s));
+ } else {
+ SHA_Simple(private_blob, private_blob_len, binary);
+ }
for (i = 0; i < 20; i++)
- sprintf(realhash + 2 * i, "%02x", binary[i]);
+ sprintf(realmac + 2 * i, "%02x", binary[i]);
- if (strcmp(hash, realhash)) {
- /* An incorrect hash is an unconditional Error if the key is
+ if (strcmp(mac, realmac)) {
+ /* An incorrect MAC is an unconditional Error if the key is
* unencrypted. Otherwise, it means Wrong Passphrase. */
ret = cipher ? SSH2_WRONG_PASSPHRASE : NULL;
goto error;
}
}
- sfree(hash);
+ sfree(mac);
/*
* Create and return the key.
fclose(fp);
if (comment)
sfree(comment);
- if (hash)
- sfree(hash);
+ if (mac)
+ sfree(mac);
if (public_blob)
sfree(public_blob);
if (private_blob)
/* Select key algorithm structure. Currently only ssh-rsa. */
if (!strcmp(b, "ssh-rsa"))
alg = &ssh_rsa;
+ else if (!strcmp(b, "ssh-dss"))
+ alg = &ssh_dss;
else {
sfree(b);
goto error;
int pub_blob_len, priv_blob_len, priv_encrypted_len;
int passlen;
int cipherblk;
- int i;
+ int i, is_mac;
char *cipherstr;
- unsigned char priv_hash[20];
+ unsigned char priv_mac[20];
/*
* Fetch the key component blobs.
memcpy(priv_blob_encrypted, priv_blob, priv_blob_len);
/* Create padding based on the SHA hash of the unpadded blob. This prevents
* too easy a known-plaintext attack on the last block. */
- SHA_Simple(priv_blob, priv_blob_len, priv_hash);
+ SHA_Simple(priv_blob, priv_blob_len, priv_mac);
assert(priv_encrypted_len - priv_blob_len < 20);
- memcpy(priv_blob_encrypted + priv_blob_len, priv_hash,
+ memcpy(priv_blob_encrypted + priv_blob_len, priv_mac,
priv_encrypted_len - priv_blob_len);
- /* Now create the _real_ private hash. */
- SHA_Simple(priv_blob_encrypted, priv_encrypted_len, priv_hash);
+ /* Now create the private MAC. */
+ if (passphrase) {
+ SHA_State s;
+ unsigned char mackey[20];
+ char header[] = "putty-private-key-file-mac-key";
+
+ passlen = strlen(passphrase);
+
+ SHA_Init(&s);
+ SHA_Bytes(&s, header, sizeof(header)-1);
+ SHA_Bytes(&s, passphrase, passlen);
+ SHA_Final(&s, mackey);
+
+ hmac_sha1_simple(mackey, 20,
+ priv_blob_encrypted, priv_encrypted_len,
+ priv_mac);
+ is_mac = 1;
+
+ memset(mackey, 0, sizeof(mackey));
+ memset(&s, 0, sizeof(s));
+ } else {
+ SHA_Simple(priv_blob_encrypted, priv_encrypted_len, priv_mac);
+ is_mac = 0;
+ }
if (passphrase) {
char key[40];
SHA_Final(&s, key + 20);
aes256_encrypt_pubkey(key, priv_blob_encrypted,
priv_encrypted_len);
+
+ memset(key, 0, sizeof(key));
+ memset(&s, 0, sizeof(s));
}
fp = fopen(filename, "w");
base64_encode(fp, pub_blob, pub_blob_len);
fprintf(fp, "Private-Lines: %d\n", base64_lines(priv_encrypted_len));
base64_encode(fp, priv_blob_encrypted, priv_encrypted_len);
- fprintf(fp, "Private-Hash: ");
+ if (is_mac)
+ fprintf(fp, "Private-MAC: ");
+ else
+ fprintf(fp, "Private-Hash: ");
for (i = 0; i < 20; i++)
- fprintf(fp, "%02x", priv_hash[i]);
+ fprintf(fp, "%02x", priv_mac[i]);
fprintf(fp, "\n");
fclose(fp);
return 1;
#define RSA_EXPONENT 37 /* we like this prime */
-#if 0 /* bignum diagnostic function */
-static void diagbn(char *prefix, Bignum md)
-{
- int i, nibbles, morenibbles;
- static const char hex[] = "0123456789ABCDEF";
-
- printf("%s0x", prefix ? prefix : "");
-
- nibbles = (3 + bignum_bitcount(md)) / 4;
- if (nibbles < 1)
- nibbles = 1;
- morenibbles = 4 * md[0] - nibbles;
- for (i = 0; i < morenibbles; i++)
- putchar('-');
- for (i = nibbles; i--;)
- putchar(hex[(bignum_byte(md, i / 2) >> (4 * (i % 2))) & 0xF]);
-
- if (prefix)
- putchar('\n');
-}
-#endif
-
int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
void *pfnparam)
{
* time. We do this in 16-bit fixed point, so 29.34 becomes
* 0x1D.57C4.
*/
- pfn(pfnparam, -1, -0x1D57C4 / (bits / 2));
- pfn(pfnparam, -2, -0x1D57C4 / (bits - bits / 2));
- pfn(pfnparam, -3, 5);
+ pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x10000);
+ pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / (bits / 2));
+ pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x10000);
+ pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / (bits - bits / 2));
+ pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x4000);
+ pfn(pfnparam, PROGFN_LIN_PHASE, 3, 5);
+ pfn(pfnparam, PROGFN_READY, 0, 0);
/*
* We don't generate e; we just use a standard one always.
*/
- key->exponent = bignum_from_short(RSA_EXPONENT);
+ key->exponent = bignum_from_long(RSA_EXPONENT);
/*
* Generate p and q: primes with combined length `bits', not
* general that's slightly more fiddly to arrange. By choosing
* a prime e, we can simplify the criterion.)
*/
- key->p = primegen(bits / 2, RSA_EXPONENT, 1, 1, pfn, pfnparam);
- key->q = primegen(bits - bits / 2, RSA_EXPONENT, 1, 2, pfn, pfnparam);
+ key->p = primegen(bits / 2, RSA_EXPONENT, 1, NULL,
+ 1, pfn, pfnparam);
+ key->q = primegen(bits - bits / 2, RSA_EXPONENT, 1, NULL,
+ 2, pfn, pfnparam);
/*
* Ensure p > q, by swapping them if not.
* the other helpful quantities: n=pq, d=e^-1 mod (p-1)(q-1),
* and (q^-1 mod p).
*/
- pfn(pfnparam, 3, 1);
+ pfn(pfnparam, PROGFN_PROGRESS, 3, 1);
key->modulus = bigmul(key->p, key->q);
- pfn(pfnparam, 3, 2);
+ pfn(pfnparam, PROGFN_PROGRESS, 3, 2);
pm1 = copybn(key->p);
decbn(pm1);
qm1 = copybn(key->q);
decbn(qm1);
phi_n = bigmul(pm1, qm1);
- pfn(pfnparam, 3, 3);
+ pfn(pfnparam, PROGFN_PROGRESS, 3, 3);
freebn(pm1);
freebn(qm1);
key->private_exponent = modinv(key->exponent, phi_n);
- pfn(pfnparam, 3, 4);
+ pfn(pfnparam, PROGFN_PROGRESS, 3, 4);
key->iqmp = modinv(key->q, key->p);
- pfn(pfnparam, 3, 5);
+ pfn(pfnparam, PROGFN_PROGRESS, 3, 5);
/*
* Clean up temporary numbers.
return !memcmp(correct, blk + len, 20);
}
+void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
+ unsigned char *output) {
+ SHA_State s1, s2;
+ unsigned char intermediate[20];
+
+ sha1_key(&s1, &s2, key, keylen);
+ SHA_Bytes(&s1, data, datalen);
+ SHA_Final(&s1, intermediate);
+
+ SHA_Bytes(&s2, intermediate, 20);
+ SHA_Final(&s2, output);
+}
+
const struct ssh_mac ssh_sha1 = {
sha1_cskey, sha1_sckey,
sha1_generate,