Add support for DSA authentication in SSH2, following clever ideas
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 22 Sep 2001 20:52:21 +0000 (20:52 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 22 Sep 2001 20:52:21 +0000 (20:52 +0000)
on how to get round the problem of generating a good k.

git-svn-id: svn://svn.tartarus.org/sgt/putty@1284 cda61777-01e9-0310-a592-d414129be87e

Makefile
doc/pubkey.but
pageant.c
puttygen.c
ssh.h
sshbn.c
sshdss.c
sshprime.c
sshpubk.c
sshrsag.c
sshsha.c

index 70c34e3..8f5bbd7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -104,16 +104,16 @@ MOBJ2 = tree234.$(OBJ)
 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)
 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)
 ##-- 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
 ##-- 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
 ##-- resources putty puttytel
 PRESRC = win_res.$(RES)
 ##-- resources pageant
@@ -273,57 +273,60 @@ plink.rsp: makefile
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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
 ##--
 
 # Hack to force version.obj to be rebuilt always
index 6946fa9..fccd7b0 100644 (file)
@@ -1,4 +1,4 @@
-\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.
 
 \# FIXME: passphrases, examples (e.g what does a key for pasting into
 \# authorized_keys look like?), index entries, links.
@@ -59,7 +59,37 @@ shuts down, without ever having stored your decrypted private key on
 disk. Many people feel this is a good compromise between security
 and convenience. See \k{pageant} for further details.
 
 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
 
 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
@@ -73,8 +103,9 @@ existing private key.
 \S{pubkey-puttygen-generate} Generate a new key
 
 Before generating a new key you have to choose the strength of the
 \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
 
 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
@@ -116,16 +147,36 @@ private key this way. Just modify the values and \e{Save} the key.
 
 Connect to your SSH server using PuTTY with the SSH protocol. When the
 connection succeeds you will be prompted for your user name and
 
 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}.
index 6ab373e..a7efcad 100644 (file)
--- a/pageant.c
+++ b/pageant.c
@@ -65,7 +65,7 @@ int agent_exists(void);
  * pads its data with random bytes. Since we only use rsadecrypt()
  * and the signing functions, which are deterministic, this should
  * never be called.
  * 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.
  * 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.
@@ -613,7 +613,7 @@ static void answer_msg(void *msg)
        break;
       case SSH2_AGENTC_SIGN_REQUEST:
        /*
        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.
         */
         * SSH_AGENT_FAILURE, depending on whether we have that key
         * or not.
         */
@@ -696,6 +696,8 @@ static void answer_msg(void *msg)
            /* Add further algorithm names here. */
            if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
                key->alg = &ssh_rsa;
            /* 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;
            else {
                sfree(key);
                goto failure;
index 1269968..7c3ef0d 100644 (file)
 /* ----------------------------------------------------------------------
  * Progress report code. This is really horrible :-)
  */
 /* ----------------------------------------------------------------------
  * 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 {
 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;
 };
 
     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;
 
 {
     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;
        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;
        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;
        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[];
 }
 
 extern char ver[];
@@ -291,7 +291,9 @@ struct rsa_key_thread_params {
     HWND progressbar;                 /* notify this with progress */
     HWND dialog;                      /* notify this on completion */
     int keysize;                      /* bits in key */
     HWND progressbar;                 /* notify this with progress */
     HWND dialog;                      /* notify this on completion */
     int keysize;                      /* bits in key */
+    int is_dsa;
     struct RSAKey *key;
     struct RSAKey *key;
+    struct dss_key *dsskey;
 };
 static DWORD WINAPI generate_rsa_key_thread(void *param)
 {
 };
 static DWORD WINAPI generate_rsa_key_thread(void *param)
 {
@@ -300,7 +302,10 @@ static DWORD WINAPI generate_rsa_key_thread(void *param)
     struct progress prog;
     prog.progbar = params->progressbar;
 
     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);
 
 
     PostMessage(params->dialog, WM_DONEKEY, 0, 0);
 
@@ -314,11 +319,12 @@ struct MainDlgState {
     int key_exists;
     int entropy_got, entropy_required, entropy_size;
     int keysize;
     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;
     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)
 };
 
 static void hidemany(HWND hwnd, const int *ids, int hideit)
@@ -465,7 +471,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        IDC_LOADSTATIC, IDC_LOAD,
        IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
        IDC_BOX_PARAMS,
        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,
     };
        IDC_BITSSTATIC, IDC_BITS,
        IDC_ABOUT,
     };
@@ -511,7 +517,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        {
            struct ctlpos cp, cp2;
 
        {
            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",
 
            ctlposinit(&cp, hwnd, 10, 10, 10);
            bartitle(&cp, "Public and private key generation for PuTTY",
@@ -547,9 +553,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                       "&Save private key", IDC_SAVE);
            endbox(&cp);
            beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
                       "&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,
                      "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);
            staticedit(&cp, "Number of &bits in a generated key:",
                       IDC_BITSSTATIC, IDC_BITS, 20);
            endbox(&cp);
@@ -599,7 +606,9 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
                params->dialog = hwnd;
                params->keysize = state->keysize;
                params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
                params->dialog = hwnd;
                params->keysize = state->keysize;
+               params->is_dsa = state->is_dsa;
                params->key = &state->key;
                params->key = &state->key;
+               params->dsskey = &state->dsskey;
 
                if (!CreateThread(NULL, 0, generate_rsa_key_thread,
                                  params, 0, &threadid)) {
 
                if (!CreateThread(NULL, 0, generate_rsa_key_thread,
                                  params, 0, &threadid)) {
@@ -652,6 +661,7 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
                    state->keysize = DEFAULT_KEYSIZE;
                /* If we ever introduce a new key type, check it here! */
                state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
                    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"
                if (state->keysize < 256) {
                    int ret = MessageBox(hwnd,
                                         "PuTTYgen will not generate a key"
@@ -937,8 +947,9 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
        state->generation_thread_exists = FALSE;
        state->key_exists = TRUE;
        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_GENERATE), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
@@ -947,8 +958,13 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
        EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
        EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
        if (state->ssh2) {
        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;
            state->commentptr = &state->ssh2key.comment;
        } else {
            state->commentptr = &state->key.comment;
@@ -965,7 +981,10 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
            struct tm *tm;
            time(&t);
            tm = localtime(&t);
            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);
        }
 
        /*
        }
 
        /*
diff --git a/ssh.h b/ssh.h
index 2406bd6..0d9613b 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -2,6 +2,7 @@
 
 #include "puttymem.h"
 #include "network.h"
 
 #include "puttymem.h"
 #include "network.h"
+#include "int64.h"
 
 struct ssh_channel;
 
 
 struct ssh_channel;
 
@@ -48,6 +49,10 @@ struct RSAKey {
     char *comment;
 };
 
     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 makekey(unsigned char *data, struct RSAKey *result,
            unsigned char **keystr, int order);
 int makeprivate(unsigned char *data, struct RSAKey *result);
@@ -92,12 +97,25 @@ typedef struct {
     int blkused;
     uint32 lenhi, lenlo;
 } SHA_State;
     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 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);
 struct ssh_cipher {
     void (*sesskey) (unsigned char *key);      /* for ssh 1 */
     void (*encrypt) (unsigned char *blk, int len);
@@ -219,7 +237,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
 Bignum copybn(Bignum b);
 Bignum bn_power_2(int n);
 void bn_restore_invariant(Bignum b);
 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);
 void freebn(Bignum b);
 Bignum modpow(Bignum base, Bignum exp, Bignum mod);
 Bignum modmul(Bignum a, Bignum b, Bignum mod);
@@ -238,6 +256,9 @@ Bignum biggcd(Bignum a, Bignum b);
 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);
 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);
 Bignum modinv(Bignum number, Bignum modulus);
 Bignum bignum_bitmask(Bignum number);
 Bignum bignum_rshift(Bignum number, int shift);
@@ -280,12 +301,19 @@ void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk,
 /*
  * For progress updates in the key generation utility.
  */
 /*
  * 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);
 
 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);
 
 
 /*
 
 
 /*
diff --git a/sshbn.c b/sshbn.c
index 0d0fce3..505a66c 100644 (file)
--- a/sshbn.c
+++ b/sshbn.c
@@ -6,13 +6,7 @@
 #include <stdlib.h>
 #include <string.h>
 
 #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;
 
 #define BIGNUM_INTERNAL
 typedef unsigned short *Bignum;
@@ -409,9 +403,10 @@ Bignum modmul(Bignum p, Bignum q, Bignum mod)
  * 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.
  * 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;
 {
     unsigned short *n, *m;
     int mshift;
@@ -460,9 +455,11 @@ void bigmod(Bignum p, Bignum mod, Bignum result, Bignum quotient)
     }
 
     /* Copy result to buffer */
     }
 
     /* 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 */
     }
 
     /* Free temporary arrays */
@@ -749,16 +746,17 @@ Bignum bignum_bitmask(Bignum n)
 }
 
 /*
 }
 
 /*
- * 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;
 
 {
     Bignum ret;
 
-    ret = newbn(2);
+    ret = newbn(3);
     ret[1] = n & 0xFFFF;
     ret[2] = (n >> 16) & 0xFFFF;
     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;
 }
 
     return ret;
 }
 
@@ -804,21 +802,40 @@ void diagbn(char *prefix, Bignum md)
     int i, nibbles, morenibbles;
     static const char hex[] = "0123456789ABCDEF";
 
     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++)
 
     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--;)
     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)
 
     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;
 }
 
 /*
 }
 
 /*
@@ -829,12 +846,9 @@ Bignum biggcd(Bignum av, Bignum bv)
     Bignum a = copybn(av);
     Bignum b = copybn(bv);
 
     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]);
     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 (t[0] > 1 && t[t[0]] == 0)
            t[0]--;
        freebn(a);
@@ -860,7 +874,7 @@ Bignum modinv(Bignum number, Bignum modulus)
     while (bignum_cmp(b, One) != 0) {
        Bignum t = newbn(b[0]);
        Bignum q = newbn(a[0]);
     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);
        while (t[0] > 1 && t[t[0]] == 0)
            t[0]--;
        freebn(a);
index 71ad59e..b2179a8 100644 (file)
--- a/sshdss.c
+++ b/sshdss.c
@@ -3,6 +3,7 @@
 #include <assert.h>
 
 #include "ssh.h"
 #include <assert.h>
 
 #include "ssh.h"
+#include "misc.h"
 
 #define GET_32BIT(cp) \
     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
 
 #define GET_32BIT(cp) \
     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
     (cp)[2] = (unsigned char)((value) >> 8); \
     (cp)[3] = (unsigned char)(value); }
 
     (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)
 {
 
 static void getstring(char **data, int *datalen, char **p, int *length)
 {
@@ -62,10 +87,6 @@ static Bignum get160(char **data, int *datalen)
     return b;
 }
 
     return b;
 }
 
-struct dss_key {
-    Bignum p, q, g, y;
-};
-
 static void *dss_newkey(char *data, int len)
 {
     char *p;
 static void *dss_newkey(char *data, int len)
 {
     char *p;
@@ -236,14 +257,8 @@ static int dss_verifysig(void *key, char *sig, int siglen,
        }
        sig += 4, siglen -= 4;         /* skip yet another length field */
     }
        }
        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);
     r = get160(&sig, &siglen);
-    diagbn("r=", r);
     s = get160(&sig, &siglen);
     s = get160(&sig, &siglen);
-    diagbn("s=", s);
     if (!r || !s)
        return 0;
 
     if (!r || !s)
        return 0;
 
@@ -251,7 +266,6 @@ static int dss_verifysig(void *key, char *sig, int siglen,
      * Step 1. w <- s^-1 mod q.
      */
     w = modinv(s, dss->q);
      * Step 1. w <- s^-1 mod q.
      */
     w = modinv(s, dss->q);
-    diagbn("w=", w);
 
     /*
      * Step 2. u1 <- SHA(message) * w mod q.
 
     /*
      * Step 2. u1 <- SHA(message) * w mod q.
@@ -260,28 +274,20 @@ static int dss_verifysig(void *key, char *sig, int siglen,
     p = hash;
     slen = 20;
     sha = get160(&p, &slen);
     p = hash;
     slen = 20;
     sha = get160(&p, &slen);
-    diagbn("sha=", sha);
     u1 = modmul(sha, w, dss->q);
     u1 = modmul(sha, w, dss->q);
-    diagbn("u1=", u1);
 
     /*
      * Step 3. u2 <- r * w mod q.
      */
     u2 = modmul(r, w, dss->q);
 
     /*
      * 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);
 
     /*
      * 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);
     yu2p = modpow(dss->y, u2, dss->p);
-    diagbn("yu2p=", yu2p);
     gu1yu2p = modmul(gu1p, yu2p, dss->p);
     gu1yu2p = modmul(gu1p, yu2p, dss->p);
-    diagbn("gu1yu2p=", gu1yu2p);
     v = modmul(gu1yu2p, One, dss->q);
     v = modmul(gu1yu2p, One, dss->q);
-    diagbn("gu1yu2q=v=", v);
-    diagbn("r=", r);
 
     /*
      * Step 5. v should now be equal to r.
 
     /*
      * Step 5. v should now be equal to r.
@@ -347,28 +353,281 @@ static unsigned char *dss_public_blob(void *key, int *len)
 
 static unsigned char *dss_private_blob(void *key, int *len)
 {
 
 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)
 {
 }
 
 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)
 {
 }
 
 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)
 {
 }
 
 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)
 {
 }
 
 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 = {
 }
 
 const struct ssh_signkey ssh_dss = {
index e6d3b3f..8bbfb87 100644 (file)
@@ -2,6 +2,7 @@
  * Prime generation.
  */
 
  * Prime generation.
  */
 
+#include <assert.h>
 #include "ssh.h"
 
 /*
 #include "ssh.h"
 
 /*
@@ -1182,14 +1183,25 @@ static const unsigned short primes[] = {
 #define NPRIMES (sizeof(primes) / sizeof(*primes))
 
 /*
 #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;
                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;
 
     Bignum p, pm1, q, wqp, wqp2;
     int progress = 0;
 
@@ -1198,15 +1210,18 @@ Bignum primegen(int bits, int modulus, int residue,
 
   STARTOVER:
 
 
   STARTOVER:
 
-    pfn(pfnparam, phase, ++progress);
+    pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
 
     /*
      * Generate a k-bit random number with top and bottom bits set.
 
     /*
      * 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)
      */
     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();
        else {
            if (bitsleft <= 0)
                bitsleft = 8, byte = random_byte();
@@ -1216,14 +1231,26 @@ Bignum primegen(int bits, int modulus, int residue,
        }
        bignum_set_bit(p, i, v);
     }
        }
        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
 
     /*
      * 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]);
      */
     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)
     }
     moduli[NPRIMES] = modulus;
     residues[NPRIMES] = (bignum_mod_short(p, (unsigned short) modulus)
@@ -1231,11 +1258,11 @@ Bignum primegen(int bits, int modulus, int residue,
     delta = 0;
     while (1) {
        for (i = 0; i < (sizeof(moduli) / sizeof(*moduli)); i++)
     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;
                break;
        if (i < (sizeof(moduli) / sizeof(*moduli))) {   /* we broke */
            delta += 2;
-           if (delta < 2) {
+           if (delta > 65536) {
                freebn(p);
                goto STARTOVER;
            }
                freebn(p);
                goto STARTOVER;
            }
@@ -1244,7 +1271,14 @@ Bignum primegen(int bits, int modulus, int residue,
        break;
     }
     q = p;
        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);
 
     /*
     freebn(q);
 
     /*
@@ -1311,7 +1345,7 @@ Bignum primegen(int bits, int modulus, int residue,
            break;
        }
 
            break;
        }
 
-       pfn(pfnparam, phase, ++progress);
+       pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
 
        /*
         * Compute w^q mod p.
 
        /*
         * Compute w^q mod p.
index d5576e8..2d1f554 100644 (file)
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -302,64 +302,85 @@ int saversakey(char *filename, struct RSAKey *key, char *passphrase)
 
 /*
  * PuTTY's own format for SSH2 keys is as follows:
 
 /*
  * 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 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
  * 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 "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.
  * 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
  * 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
  *    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
  * 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)
  *    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:
  * 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
  *    uint32  sequence-number
- *    string  passphrase
- * 
+ *    data    passphrase
+ *
  * where the sequence-number increases from zero. As many of these
  * hashes are used as necessary.
  * 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.
  * 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
  * 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)
  */
 
 static int read_header(FILE * fp, char *header)
@@ -514,16 +535,17 @@ struct ssh2_userkey ssh2_wrong_passphrase = {
 struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
 {
     FILE *fp;
 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;
     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 */
 
     ret = NULL;                               /* return NULL for most errors */
-    comment = hash = NULL;
+    comment = mac = NULL;
     public_blob = private_blob = NULL;
 
     fp = fopen(filename, "rb");
     public_blob = private_blob = NULL;
 
     fp = fopen(filename, "rb");
@@ -536,9 +558,11 @@ struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
        goto error;
     if ((b = read_body(fp)) == NULL)
        goto error;
        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;
     if (!strcmp(b, "ssh-rsa"))
        alg = &ssh_rsa;
+    else if (!strcmp(b, "ssh-dss"))
+       alg = &ssh_dss;
     else {
        sfree(b);
        goto error;
     else {
        sfree(b);
        goto error;
@@ -588,10 +612,18 @@ struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
     if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)
        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;
        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);
        goto error;
 
     fclose(fp);
@@ -603,15 +635,12 @@ struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
     if (cipher) {
        unsigned char key[40];
        SHA_State s;
     if (cipher) {
        unsigned char key[40];
        SHA_State s;
-       int passlen;
 
        if (!passphrase)
            goto error;
        if (private_blob_len % cipherblk)
            goto error;
 
 
        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);
        SHA_Init(&s);
        SHA_Bytes(&s, "\0\0\0\0", 4);
        SHA_Bytes(&s, passphrase, passlen);
@@ -627,21 +656,41 @@ struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
      * Verify the private hash.
      */
     {
      * Verify the private hash.
      */
     {
-       char realhash[41];
+       char realmac[41];
        unsigned char binary[20];
 
        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++)
        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;
        }
     }
             * unencrypted. Otherwise, it means Wrong Passphrase. */
            ret = cipher ? SSH2_WRONG_PASSPHRASE : NULL;
            goto error;
        }
     }
-    sfree(hash);
+    sfree(mac);
 
     /*
      * Create and return the key.
 
     /*
      * Create and return the key.
@@ -668,8 +717,8 @@ struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase)
        fclose(fp);
     if (comment)
        sfree(comment);
        fclose(fp);
     if (comment)
        sfree(comment);
-    if (hash)
-       sfree(hash);
+    if (mac)
+       sfree(mac);
     if (public_blob)
        sfree(public_blob);
     if (private_blob)
     if (public_blob)
        sfree(public_blob);
     if (private_blob)
@@ -702,6 +751,8 @@ char *ssh2_userkey_loadpub(char *filename, char **algorithm,
     /* Select key algorithm structure. Currently only ssh-rsa. */
     if (!strcmp(b, "ssh-rsa"))
        alg = &ssh_rsa;
     /* 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;
     else {
        sfree(b);
        goto error;
@@ -863,9 +914,9 @@ int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
     int pub_blob_len, priv_blob_len, priv_encrypted_len;
     int passlen;
     int cipherblk;
     int pub_blob_len, priv_blob_len, priv_encrypted_len;
     int passlen;
     int cipherblk;
-    int i;
+    int i, is_mac;
     char *cipherstr;
     char *cipherstr;
-    unsigned char priv_hash[20];
+    unsigned char priv_mac[20];
 
     /*
      * Fetch the key component blobs.
 
     /*
      * Fetch the key component blobs.
@@ -895,13 +946,35 @@ int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
     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. */
     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);
     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);
 
           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];
 
     if (passphrase) {
        char key[40];
@@ -919,6 +992,9 @@ int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
        SHA_Final(&s, key + 20);
        aes256_encrypt_pubkey(key, priv_blob_encrypted,
                              priv_encrypted_len);
        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");
     }
 
     fp = fopen(filename, "w");
@@ -931,9 +1007,12 @@ int ssh2_save_userkey(char *filename, struct ssh2_userkey *key,
     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);
     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++)
     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;
     fprintf(fp, "\n");
     fclose(fp);
     return 1;
index 9e543c9..eb714ad 100644 (file)
--- a/sshrsag.c
+++ b/sshrsag.c
@@ -6,28 +6,6 @@
 
 #define RSA_EXPONENT 37                       /* we like this prime */
 
 
 #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)
 {
 int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
                 void *pfnparam)
 {
@@ -61,14 +39,18 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
      * time. We do this in 16-bit fixed point, so 29.34 becomes
      * 0x1D.57C4.
      */
      * 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.
      */
 
     /*
      * 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
 
     /*
      * Generate p and q: primes with combined length `bits', not
@@ -77,8 +59,10 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
      * general that's slightly more fiddly to arrange. By choosing
      * a prime e, we can simplify the criterion.)
      */
      * 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.
 
     /*
      * Ensure p > q, by swapping them if not.
@@ -94,21 +78,21 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
      * the other helpful quantities: n=pq, d=e^-1 mod (p-1)(q-1),
      * and (q^-1 mod p).
      */
      * 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);
     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);
     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);
     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);
     key->iqmp = modinv(key->q, key->p);
-    pfn(pfnparam, 3, 5);
+    pfn(pfnparam, PROGFN_PROGRESS, 3, 5);
 
     /*
      * Clean up temporary numbers.
 
     /*
      * Clean up temporary numbers.
index 6031685..5b50d7e 100644 (file)
--- a/sshsha.c
+++ b/sshsha.c
@@ -271,6 +271,19 @@ static int sha1_verify(unsigned char *blk, int len, unsigned long seq)
     return !memcmp(correct, blk + len, 20);
 }
 
     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,
 const struct ssh_mac ssh_sha1 = {
     sha1_cskey, sha1_sckey,
     sha1_generate,