Preliminary support for RSA user authentication in SSH2! Most of the
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 3 Mar 2001 11:54:34 +0000 (11:54 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 3 Mar 2001 11:54:34 +0000 (11:54 +0000)
error messages are currently wrong, and Pageant doesn't yet support
the new key type, and I haven't thoroughly tested that falling back
to password authentication and trying invalid keys etc all work. But
what I have here has successfully performed a public key
authentication, so it's working to at least some extent.

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

15 files changed:
pageant.c
puttygen.c
puttygen.rc
ssh.c
ssh.h
sshaes.c
sshblowf.c
sshdes.c
sshdh.c
sshdss.c
sshmd5.c
sshpubk.c
sshrsa.c
sshrsag.c
sshsha.c

index 888dd43..f4d5aa5 100644 (file)
--- a/pageant.c
+++ b/pageant.c
@@ -254,7 +254,7 @@ static void add_keyfile(char *filename) {
             }
         } else
             *passphrase = '\0';
-        ret = loadrsakey(filename, key, NULL, passphrase);
+        ret = loadrsakey(filename, key, passphrase);
         attempts++;
     } while (ret == -1);
     if (comment) sfree(comment);
index 084e353..e93781c 100644 (file)
@@ -101,6 +101,21 @@ static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
         SetForegroundWindow(hwnd);
         SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0,
                       SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+       /*
+        * Centre the window.
+        */
+       {                              /* centre the window */
+           RECT rs, rd;
+           HWND hw;
+
+           hw = GetDesktopWindow();
+           if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
+               MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
+                           (rs.bottom + rs.top + rd.top - rd.bottom)/2,
+                           rd.right-rd.left, rd.bottom-rd.top, TRUE);
+       }
+
         p = (struct PassphraseProcStruct *)lParam;
         passphrase = p->passphrase;
         if (p->comment)
@@ -176,6 +191,20 @@ static int CALLBACK LicenceProc (HWND hwnd, UINT msg,
                                 WPARAM wParam, LPARAM lParam) {
     switch (msg) {
       case WM_INITDIALOG:
+       /*
+        * Centre the window.
+        */
+       {                              /* centre the window */
+           RECT rs, rd;
+           HWND hw;
+
+           hw = GetDesktopWindow();
+           if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
+               MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
+                           (rs.bottom + rs.top + rd.top - rd.bottom)/2,
+                           rd.right-rd.left, rd.bottom-rd.top, TRUE);
+       }
+
        return 1;
       case WM_COMMAND:
        switch (LOWORD(wParam)) {
@@ -198,6 +227,20 @@ static int CALLBACK AboutProc (HWND hwnd, UINT msg,
                               WPARAM wParam, LPARAM lParam) {
     switch (msg) {
       case WM_INITDIALOG:
+       /*
+        * Centre the window.
+        */
+       {                              /* centre the window */
+           RECT rs, rd;
+           HWND hw;
+
+           hw = GetDesktopWindow();
+           if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
+               MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
+                           (rs.bottom + rs.top + rd.top - rd.bottom)/2,
+                           rd.right-rd.left, rd.bottom-rd.top, TRUE);
+       }
+
         SetDlgItemText (hwnd, 100, ver);
        return 1;
       case WM_COMMAND:
@@ -228,7 +271,6 @@ struct rsa_key_thread_params {
     HWND dialog;                       /* notify this on completion */
     int keysize;                      /* bits in key */
     struct RSAKey *key;
-    struct RSAAux *aux;
 };
 static DWORD WINAPI generate_rsa_key_thread(void *param) {
     struct rsa_key_thread_params *params =
@@ -236,8 +278,7 @@ static DWORD WINAPI generate_rsa_key_thread(void *param) {
     struct progress prog;
     prog.progbar = params->progressbar;
 
-    rsa_generate(params->key, params->aux,
-                params->keysize, progress_update, &prog);
+    rsa_generate(params->key, params->keysize, progress_update, &prog);
 
     PostMessage(params->dialog, WM_DONEKEY, 0, 0);
 
@@ -251,9 +292,11 @@ struct MainDlgState {
     int key_exists;
     int entropy_got, entropy_required, entropy_size;
     int keysize;
+    int ssh2;
+    char **commentptr;                /* points to key.comment or ssh2key.comment */
+    struct ssh2_userkey ssh2key;
     unsigned *entropy;
     struct RSAKey key;
-    struct RSAAux aux;
 };
 
 static void hidemany(HWND hwnd, const int *ids, int hideit) {
@@ -262,7 +305,7 @@ static void hidemany(HWND hwnd, const int *ids, int hideit) {
     }
 }
 
-static void setupbigedit(HWND hwnd, int id, struct RSAKey *key) {
+static void setupbigedit1(HWND hwnd, int id, struct RSAKey *key) {
     char *buffer;
     char *dec1, *dec2;
 
@@ -279,6 +322,32 @@ static void setupbigedit(HWND hwnd, int id, struct RSAKey *key) {
     sfree(buffer);
 }
 
+static void setupbigedit2(HWND hwnd, int id, struct ssh2_userkey *key) {
+    unsigned char *pub_blob;
+    char *buffer, *p;
+    int pub_len, buflen;
+    int i;
+
+    pub_blob = key->alg->public_blob(key->data, &pub_len);
+    buffer = smalloc(strlen(key->alg->name) + 4*((pub_len+2)/3) +
+                    strlen(key->comment) + 3);
+    strcpy(buffer, key->alg->name);
+    p = buffer + strlen(buffer);
+    *p++ = ' ';
+    i = 0;
+    while (i < pub_len) {
+       int n = (pub_len-i < 3 ? pub_len-i : 3);
+       base64_encode_atom(pub_blob+i, n, p);
+       i += n;
+       p += 4;
+    }
+    *p++ = ' ';
+    strcpy(p, key->comment);
+    SetDlgItemText(hwnd, id, buffer);
+    sfree(pub_blob);
+    sfree(buffer);    
+}
+
 /*
  * Dialog-box function for the main PuTTYgen dialog box.
  */
@@ -301,6 +370,7 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
         IDC_LOADSTATIC, IDC_LOAD,
         IDC_SAVESTATIC, IDC_SAVE,
         IDC_BOX_PARAMS,
+       IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA,
         IDC_BITSSTATIC, IDC_BITS,
         IDC_ABOUT,
     };
@@ -320,6 +390,20 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
 
     switch (msg) {
       case WM_INITDIALOG:
+       /*
+        * Centre the window.
+        */
+       {                              /* centre the window */
+           RECT rs, rd;
+           HWND hw;
+
+           hw = GetDesktopWindow();
+           if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
+               MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
+                           (rs.bottom + rs.top + rd.top - rd.bottom)/2,
+                           rd.right-rd.left, rd.bottom-rd.top, TRUE);
+       }
+
         state = smalloc(sizeof(*state));
         state->generation_thread_exists = FALSE;
         state->collecting_entropy = FALSE;
@@ -347,14 +431,14 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                         IDC_PKSTATIC, IDC_KEYDISPLAY, 7);
             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
             staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
-                       IDC_FINGERPRINT, 70);
+                       IDC_FINGERPRINT, 75);
             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1, 0);
             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
-                       IDC_COMMENTEDIT, 70);
+                       IDC_COMMENTEDIT, 75);
             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
-                           IDC_PASSPHRASE1EDIT, 70);
+                           IDC_PASSPHRASE1EDIT, 75);
             staticpassedit(&cp, "C&onfirm passphrase:", IDC_PASSPHRASE2STATIC,
-                           IDC_PASSPHRASE2EDIT, 70);
+                           IDC_PASSPHRASE2EDIT, 75);
             endbox(&cp);
             beginbox(&cp, "Actions",
                      IDC_BOX_ACTIONS);
@@ -367,10 +451,14 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
             endbox(&cp);
             beginbox(&cp, "Parameters",
                      IDC_BOX_PARAMS);
+           radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 2,
+                          "SSH&1 (RSA)", IDC_KEYSSH1,
+                          "SSH2 &RSA", IDC_KEYSSH2RSA, NULL);
             staticedit(&cp, "Number of &bits in a generated key:",
                       IDC_BITSSTATIC, IDC_BITS, 20);
             endbox(&cp);
         }
+       CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH1);
        SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
 
         /*
@@ -416,7 +504,6 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                 params->dialog = hwnd;
                params->keysize = state->keysize;
                 params->key = &state->key;
-                params->aux = &state->aux;
 
                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
                                   params, 0, &threadid)) {
@@ -439,10 +526,10 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                 if (state->key_exists) {
                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
                     int len = GetWindowTextLength(editctl);
-                    if (state->key.comment)
-                        sfree(state->key.comment);
-                    state->key.comment = smalloc(len+1);
-                    GetWindowText(editctl, state->key.comment, len+1);
+                    if (*state->commentptr)
+                        sfree(*state->commentptr);
+                    *state->commentptr = smalloc(len+1);
+                    GetWindowText(editctl, *state->commentptr, len+1);
                 }                
             }
            break;
@@ -459,6 +546,8 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS,
                                                &ok, FALSE);
                 if (!ok) state->keysize = DEFAULT_KEYSIZE;
+               /* If we ever introduce a new key type, check it here! */
+               state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
                 if (state->keysize < 256) {
                     int ret = MessageBox(hwnd,
                                          "PuTTYgen will not generate a key"
@@ -546,8 +635,13 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                        if (ret != IDYES)
                            break;
                    }
-                    ret = saversakey(filename, &state->key, &state->aux,
-                                    *passphrase ? passphrase : NULL);
+                   if (state->ssh2) {
+                       ret = ssh2_save_userkey(filename, &state->ssh2key,
+                                               *passphrase ? passphrase : NULL);
+                   } else {
+                       ret = saversakey(filename, &state->key,
+                                        *passphrase ? passphrase : NULL);
+                   }
                    if (ret <= 0) {
                        MessageBox(hwnd, "Unable to save key file",
                                   "PuTTYgen Error",
@@ -564,13 +658,25 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                                    filename, 0)) {
                     char passphrase[PASSPHRASE_MAXLEN];
                     int needs_pass;
+                   int ver;
                     int ret;
                     char *comment;
                     struct PassphraseProcStruct pps;
-                    struct RSAKey newkey;
-                    struct RSAAux newaux;
+                    struct RSAKey newkey1;
+                   struct ssh2_userkey *newkey2;
+
+                   ver = keyfile_version(filename);
+                   if (ver == 0) {
+                        MessageBox(NULL, "Couldn't load private key.",
+                                   "PuTTYgen Error", MB_OK | MB_ICONERROR);
+                       break;
+                   }
 
-                    needs_pass = rsakey_encrypted(filename, &comment);
+                   comment = NULL;
+                   if (ver == 1)
+                       needs_pass = rsakey_encrypted(filename, &comment);
+                   else
+                       needs_pass = ssh2_userkey_encrypted(filename, &comment);
                     pps.passphrase = passphrase;
                     pps.comment = comment;
                     do {
@@ -586,17 +692,23 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                             }
                         } else
                             *passphrase = '\0';
-                        ret = loadrsakey(filename, &newkey, &newaux,
-                                         passphrase);
+                       if (ver == 1)
+                           ret = loadrsakey(filename, &newkey1, passphrase);
+                       else {
+                           newkey2 = ssh2_load_userkey(filename, passphrase);
+                           if (newkey2 == SSH2_WRONG_PASSPHRASE)
+                               ret = -1;
+                           else if (!newkey2)
+                               ret = 0;
+                           else
+                               ret = 1;
+                       }
                     } while (ret == -1);
                     if (comment) sfree(comment);
                     if (ret == 0) {
                         MessageBox(NULL, "Couldn't load private key.",
                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
                     } else if (ret == 1) {
-                        state->key = newkey;
-                        state->aux = newaux;
-
                         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
                         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
                         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
@@ -605,29 +717,55 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
                          * key data.
                          */
                         {
-                            char buf[128];
                             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
                                            passphrase);
                             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
                                            passphrase);
-                            SetDlgItemText(hwnd, IDC_COMMENTEDIT,
-                                           state->key.comment);
-                            /*
-                             * Set the key fingerprint.
-                             */
-                            {
-                                char *savecomment = state->key.comment;
+                           if (ver == 1) {
+                               char buf[128];
+                               char *savecomment;
+
+                               state->ssh2 = FALSE;
+                               state->commentptr = &state->key.comment;
+                               state->key = newkey1;
+
+                               /*
+                                * Set the key fingerprint.
+                                */
+                               savecomment = state->key.comment;
                                 state->key.comment = NULL;
                                 rsa_fingerprint(buf, sizeof(buf), &state->key);
                                 state->key.comment = savecomment;
-                            }
-                            SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
-                            /*
-                             * Construct a decimal representation
-                             * of the key, for pasting into
-                             * .ssh/authorized_keys on a Unix box.
-                             */
-                            setupbigedit(hwnd, IDC_KEYDISPLAY, &state->key);
+
+                               SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
+                               /*
+                                * Construct a decimal representation
+                                * of the key, for pasting into
+                                * .ssh/authorized_keys on a Unix box.
+                                */
+                               setupbigedit1(hwnd, IDC_KEYDISPLAY, &state->key);
+                           } else {
+                               char *fp;
+                               char *savecomment;
+
+                               state->ssh2 = TRUE;
+                               state->commentptr = &state->ssh2key.comment;
+                               state->ssh2key = *newkey2;   /* structure copy */
+                               sfree(newkey2);
+
+                               savecomment = state->ssh2key.comment;
+                                state->ssh2key.comment = NULL;
+                               fp = state->
+                                   ssh2key.alg->fingerprint(state->ssh2key.data);
+                                state->ssh2key.comment = savecomment;
+
+                               SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
+                               sfree(fp);
+
+                               setupbigedit2(hwnd, IDC_KEYDISPLAY, &state->ssh2key);
+                           }
+                            SetDlgItemText(hwnd, IDC_COMMENTEDIT,
+                                           *state->commentptr);
                         }
                         /*
                          * Finally, hide the progress bar and show
@@ -651,26 +789,30 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
+       if (state->ssh2)
+           state->commentptr = &state->ssh2key.comment;
+       else
+           state->commentptr = &state->key.comment;
         /*
          * Invent a comment for the key. We'll do this by including
          * the date in it. This will be so horrifyingly ugly that
          * the user will immediately want to change it, which is
          * what we want :-)
          */
-        state->key.comment = smalloc(30);
+        *state->commentptr = smalloc(30);
         {
             time_t t;
             struct tm *tm;
             time(&t);
             tm = localtime(&t);
-            strftime(state->key.comment, 30, "rsa-key-%Y%m%d", tm);
+            strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
         }
             
         /*
          * Now update the key controls with all the key data.
          */
         {
-            char buf[128];
+           char *savecomment;
             /*
              * Blank passphrase, initially. This isn't dangerous,
              * because we will warn (Are You Sure?) before allowing
@@ -681,22 +823,32 @@ static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
             /*
              * Set the comment.
              */
-            SetDlgItemText(hwnd, IDC_COMMENTEDIT, state->key.comment);
+            SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
             /*
              * Set the key fingerprint.
              */
-            {
-                char *savecomment = state->key.comment;
-                state->key.comment = NULL;
+           savecomment = *state->commentptr;
+           *state->commentptr = NULL;
+            if (state->ssh2) {
+               char *fp;
+                fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
+               SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
+               sfree(fp);
+           } else {
+               char buf[128];
                 rsa_fingerprint(buf, sizeof(buf), &state->key);
-                state->key.comment = savecomment;
+               SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
             }
-            SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
+           *state->commentptr = savecomment;
             /*
              * Construct a decimal representation of the key, for
              * pasting into .ssh/authorized_keys on a Unix box.
              */
-            setupbigedit(hwnd, IDC_KEYDISPLAY, &state->key);
+           if (state->ssh2) {
+               setupbigedit2(hwnd, IDC_KEYDISPLAY, &state->ssh2key);
+           } else {
+               setupbigedit1(hwnd, IDC_KEYDISPLAY, &state->key);
+           }
         }
         /*
          * Finally, hide the progress bar and show the key data.
index a1df423..f98393b 100644 (file)
@@ -5,7 +5,7 @@
 
 200 ICON "puttygen.ico"
 
-201 DIALOG DISCARDABLE 0, 0, 300, 276
+201 DIALOG DISCARDABLE 0, 0, 330, 296
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "PuTTY Key Generator"
 FONT 8, "MS Sans Serif"
diff --git a/ssh.c b/ssh.c
index a0309c0..405ae90 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -164,13 +164,6 @@ enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM };
 #define crWaitUntil(c) do { crReturn(0); } while (!(c))
 #define crWaitUntilV(c)        do { crReturnV; } while (!(c))
 
-extern const struct ssh_cipher ssh_3des;
-extern const struct ssh2_ciphers ssh2_3des;
-extern const struct ssh_cipher ssh_des;
-extern const struct ssh2_ciphers ssh2_aes;
-extern const struct ssh_cipher ssh_blowfish_ssh1;
-extern const struct ssh2_ciphers ssh2_blowfish;
-
 extern char *x11_init (Socket *, char *, void *);
 extern void x11_close (Socket);
 extern void x11_send  (Socket , char *, int);
@@ -188,20 +181,14 @@ const static struct ssh2_ciphers *ciphers[] = {
     &ssh2_3des,
 };
 
-extern const struct ssh_kex ssh_diffiehellman;
-extern const struct ssh_kex ssh_diffiehellman_gex;
 const static struct ssh_kex *kex_algs[] = {
 #ifdef DO_DIFFIE_HELLMAN_GEX
     &ssh_diffiehellman_gex,
 #endif
     &ssh_diffiehellman };
 
-extern const struct ssh_signkey ssh_dss;
-extern const struct ssh_signkey ssh_rsa;
 const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
 
-extern const struct ssh_mac ssh_md5, ssh_sha1, ssh_sha1_buggy;
-
 static void nullmac_key(unsigned char *key) { }
 static void nullmac_generate(unsigned char *blk, int len, unsigned long seq) { }
 static int nullmac_verify(unsigned char *blk, int len, unsigned long seq) { return 1; }
@@ -283,6 +270,7 @@ static const struct ssh_compress *cscomp = NULL;
 static const struct ssh_compress *sccomp = NULL;
 static const struct ssh_kex *kex = NULL;
 static const struct ssh_signkey *hostkey = NULL;
+static unsigned char ssh2_session_id[20];
 int (*ssh_get_password)(const char *prompt, char *str, int maxlen) = NULL;
 
 static char *savedhost;
@@ -1047,6 +1035,14 @@ static unsigned long ssh2_pkt_getuint32(void) {
     pktin.savedpos += 4;
     return value;
 }
+static int ssh2_pkt_getbool(void) {
+    unsigned long value;
+    if (pktin.length - pktin.savedpos < 1)
+        return 0;                      /* arrgh, no way to decline (FIXME?) */
+    value = pktin.data[pktin.savedpos] != 0;
+    pktin.savedpos++;
+    return value;
+}
 static void ssh2_pkt_getstring(char **p, int *length) {
     *p = NULL;
     if (pktin.length - pktin.savedpos < 4)
@@ -1738,7 +1734,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
             static unsigned char buffer[32];
 
             tried_publickey = 1;
-            i = loadrsakey(cfg.keyfile, &pubkey, NULL, password);
+            i = loadrsakey(cfg.keyfile, &pubkey, password);
             if (i == 0) {
                 c_write("Couldn't load public key from ", 30);
                 c_write(cfg.keyfile, strlen(cfg.keyfile));
@@ -2200,7 +2196,6 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     static int hostkeylen, siglen;
     static void *hkey;                /* actual host key */
     static unsigned char exchange_hash[20];
-    static unsigned char first_exchange_hash[20];
     static unsigned char keyspace[40];
     static const struct ssh2_ciphers *preferred_cipher;
     static const struct ssh_compress *preferred_comp;
@@ -2543,18 +2538,18 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
      * _first_ key exchange.
      */
     if (first_kex)
-       memcpy(first_exchange_hash, exchange_hash, sizeof(exchange_hash));
-    ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'C', keyspace);
+       memcpy(ssh2_session_id, exchange_hash, sizeof(exchange_hash));
+    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'C', keyspace);
     cscipher->setcskey(keyspace);
-    ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'D', keyspace);
+    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'D', keyspace);
     sccipher->setsckey(keyspace);
-    ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'A', keyspace);
+    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'A', keyspace);
     cscipher->setcsiv(keyspace);
-    ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'B', keyspace);
+    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'B', keyspace);
     sccipher->setsciv(keyspace);
-    ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'E', keyspace);
+    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'E', keyspace);
     csmac->setcskey(keyspace);
-    ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'F', keyspace);
+    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'F', keyspace);
     scmac->setsckey(keyspace);
 
     /*
@@ -2631,6 +2626,13 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 {
     static unsigned long remote_winsize;
     static unsigned long remote_maxpkt;
+    static enum {
+       AUTH_INVALID, AUTH_PUBLICKEY_AGENT, AUTH_PUBLICKEY_FILE, AUTH_PASSWORD
+    } method;
+    static int gotit, need_pw, can_pubkey, can_passwd, tried_pubkey_config;
+    static char username[100];
+    static char pwprompt[200];
+    static char password[100];
 
     crBegin;
 
@@ -2647,22 +2649,15 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     }
 
     /*
-     * FIXME: currently we support only password authentication.
-     * (This places us technically in violation of the SSH2 spec.
-     * We must fix this.)
+     * Get a username.
      */
-    while (1) {
-        /*
-         * Get a username and a password.
-         */
-       static char username[100];
-       static char password[100];
+    {
        static int pos = 0;
        static char c;
 
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
            c_write("login as: ", 10);
-            ssh_send_ok = 1;
+           ssh_send_ok = 1;
            while (pos >= 0) {
                crWaitUntilV(!ispkt);
                while (inlen--) switch (c = *in++) {
@@ -2688,7 +2683,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    break;
                  default:
                    if (((c >= ' ' && c <= '~') ||
-                         ((unsigned char)c >= 160)) && pos < 40) {
+                        ((unsigned char)c >= 160)) && pos < 40) {
                        username[pos++] = c;
                        c_write(&c, 1);
                    }
@@ -2701,106 +2696,296 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            char stuff[200];
            strncpy(username, cfg.username, 99);
            username[99] = '\0';
-            if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
+           if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
                sprintf(stuff, "Using username \"%s\".\r\n", username);
                c_write(stuff, strlen(stuff));
            }
        }
+    }
 
-       if (ssh_get_password) {
-           char prompt[200];
-           sprintf(prompt, "%.90s@%.90s's password: ", username, savedhost);
-           if (!ssh_get_password(prompt, password, sizeof(password))) {
-                /*
-                 * get_password failed to get a password (for
-                 * example because one was supplied on the command
-                 * line which has already failed to work).
-                 * Terminate.
-                 */
-                logevent("No more passwords to try");
-                ssh_state = SSH_STATE_CLOSED;
-                crReturnV;
-            }
-       } else {
-            c_write("password: ", 10);
-            ssh_send_ok = 1;
+    /*
+     * Send an authentication request using method "none": (a)
+     * just in case it succeeds, and (b) so that we know what
+     * authentication methods we can usefully try next.
+     */
+    ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+    ssh2_pkt_addstring(username);
+    ssh2_pkt_addstring("ssh-connection");   /* service requested */
+    ssh2_pkt_addstring("none");    /* method */
+    ssh2_pkt_send();
+    logevent("Attempting null authentication");
+    gotit = FALSE;
 
-            pos = 0;
-            while (pos >= 0) {
-                crWaitUntilV(!ispkt);
-                while (inlen--) switch (c = *in++) {
-                  case 10: case 13:
-                    password[pos] = 0;
-                    pos = -1;
-                    break;
-                  case 8: case 127:
-                    if (pos > 0)
-                        pos--;
-                    break;
-                  case 21: case 27:
-                    pos = 0;
-                    break;
-                  case 3: case 4:
-                    random_save_seed();
-                    exit(0);
-                    break;
-                  default:
-                    if (((c >= ' ' && c <= '~') ||
-                         ((unsigned char)c >= 160)) && pos < 40)
-                        password[pos++] = c;
-                    break;
-                }
-            }
-            c_write("\r\n", 2);
+    while (1) {
+       /*
+        * Wait for the result of the last authentication request.
+        */
+       if (!gotit)
+           crWaitUntilV(ispkt);
+       while (pktin.type == SSH2_MSG_USERAUTH_BANNER) {
+           /* FIXME: should support this */
+           crWaitUntilV(ispkt);
        }
-
-        /*
-         * We send the password packet lumped tightly together with
-         * an SSH_MSG_IGNORE packet. The IGNORE packet contains a
-         * string long enough to make the total length of the two
-         * packets constant. This should ensure that a passive
-         * listener doing traffic analyis can't work out the length
-         * of the password.
-         * 
-         * For this to work, we need an assumption about the
-         * maximum length of the password packet. I think 256 is
-         * pretty conservative. Anyone using a password longer than
-         * that probably doesn't have much to worry about from
-         * people who find out how long their password is!
-         */
-        ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-        ssh2_pkt_addstring(username);
-        ssh2_pkt_addstring("ssh-connection");   /* service requested */
-        ssh2_pkt_addstring("password");
-        ssh2_pkt_addbool(FALSE);
-        ssh2_pkt_addstring(password);
-        ssh2_pkt_defer();
-        /*
-         * We'll include a string that's an exact multiple of the
-         * cipher block size. If the cipher is NULL for some
-         * reason, we don't do this trick at all because we gain
-         * nothing by it.
-         */
-        if (cscipher) {
-            int i, j;
-            ssh2_pkt_init(SSH2_MSG_IGNORE);
-            ssh2_pkt_addstring_start();
-            for (i = deferred_len; i <= 256; i += cscipher->blksize) {
-                for (j = 0; j < cscipher->blksize; j++) {
-                    char c = (char)random_byte();
-                    ssh2_pkt_addstring_data(&c, 1);
-                }
-            }
-            ssh2_pkt_defer();
+        if (pktin.type == SSH2_MSG_USERAUTH_SUCCESS) {
+           logevent("Access granted");
+           break;
         }
-        ssh2_pkt_defersend();
 
-        crWaitUntilV(ispkt);
-        if (pktin.type != SSH2_MSG_USERAUTH_SUCCESS) {
-           c_write("Access denied\r\n", 15);
-           logevent("Authentication refused");
-        } else
-            break;
+       if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
+           bombout(("Strange packet received during authentication: type %d",
+                    pktin.type));
+       }
+
+       gotit = FALSE;
+
+       /*
+        * OK, we're now sitting on a USERAUTH_FAILURE message, so
+        * we can look at the string in it and know what we can
+        * helpfully try next.
+        */
+       {
+           char *methods;
+           int methlen;
+           ssh2_pkt_getstring(&methods, &methlen);
+           if (!ssh2_pkt_getbool()) {
+               /*
+                * FIXME: these messages are often inappropriate.
+                */
+               c_write("Access denied\r\n", 15);
+               logevent("Access denied");
+           } else {
+               c_write("Authentication partially successful\r\n", 37);
+               logevent("Authentication partially successful");
+           }
+
+           can_pubkey = in_commasep_string("publickey", methods, methlen);
+           can_passwd = in_commasep_string("password", methods, methlen);
+       }
+
+       method = 0;
+
+       if (!method && can_pubkey && *cfg.keyfile && !tried_pubkey_config) {
+           unsigned char *pub_blob;
+           char *algorithm, *comment;
+           int pub_blob_len;
+
+           tried_pubkey_config = TRUE;
+
+           /*
+            * Try the public key supplied in the configuration.
+            * 
+            * First, offer the public blob to see if the server is
+            * willing to accept it.
+            */
+           pub_blob = ssh2_userkey_loadpub(cfg.keyfile, &algorithm,
+                                           &pub_blob_len);
+           if (pub_blob) {
+               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(username);
+               ssh2_pkt_addstring("ssh-connection");   /* service requested */
+               ssh2_pkt_addstring("publickey");/* method */
+               ssh2_pkt_addbool(FALSE);   /* no signature included */
+               ssh2_pkt_addstring(algorithm);
+               ssh2_pkt_addstring_start();
+               ssh2_pkt_addstring_data(pub_blob, pub_blob_len);
+               ssh2_pkt_send();
+               logevent("Offered public key"); /* FIXME */
+
+               crWaitUntilV(ispkt);
+               if (pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
+                   gotit = TRUE;
+                   continue;          /* key refused; give up on it */
+               }
+
+               logevent("Offer of public key accepted");
+               /*
+                * Actually attempt a serious authentication using
+                * the key.
+                */
+               if (ssh2_userkey_encrypted(cfg.keyfile, &comment)) {
+                   sprintf(pwprompt, "Passphrase for key \"%.100s\": ", comment);
+                   need_pw = TRUE;
+               } else {
+                   need_pw = FALSE;
+               }
+               method = AUTH_PUBLICKEY_FILE;
+           }
+       }
+
+       if (!method && can_passwd) {
+           method = AUTH_PASSWORD;
+           sprintf(pwprompt, "%.90s@%.90s's password: ", username, savedhost);
+           need_pw = TRUE;
+       }
+
+       if (need_pw) {
+           if (ssh_get_password) {
+               if (!ssh_get_password(pwprompt, password, sizeof(password))) {
+                   /*
+                    * get_password failed to get a password (for
+                    * example because one was supplied on the command
+                    * line which has already failed to work).
+                    * Terminate.
+                    */
+                   logevent("No more passwords to try");
+                   ssh_state = SSH_STATE_CLOSED;
+                   crReturnV;
+               }
+           } else {
+               static int pos = 0;
+               static char c;
+
+               c_write(pwprompt, strlen(pwprompt));
+               ssh_send_ok = 1;
+
+               pos = 0;
+               while (pos >= 0) {
+                   crWaitUntilV(!ispkt);
+                   while (inlen--) switch (c = *in++) {
+                     case 10: case 13:
+                       password[pos] = 0;
+                       pos = -1;
+                       break;
+                     case 8: case 127:
+                       if (pos > 0)
+                           pos--;
+                       break;
+                     case 21: case 27:
+                       pos = 0;
+                       break;
+                     case 3: case 4:
+                       random_save_seed();
+                       exit(0);
+                       break;
+                     default:
+                       if (((c >= ' ' && c <= '~') ||
+                            ((unsigned char)c >= 160)) && pos < 40)
+                           password[pos++] = c;
+                       break;
+                   }
+               }
+               c_write("\r\n", 2);
+           }
+       }
+
+       if (method == AUTH_PUBLICKEY_FILE) {
+           /*
+            * We have our passphrase. Now try the actual authentication.
+            */
+           struct ssh2_userkey *key;
+
+           key = ssh2_load_userkey(cfg.keyfile, password);
+           if (key == SSH2_WRONG_PASSPHRASE) {
+               c_write("Wrong passphrase\r\n", 18);
+               tried_pubkey_config = FALSE;
+               /* Send a spurious AUTH_NONE to return to the top. */
+               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(username);
+               ssh2_pkt_addstring("ssh-connection");   /* service requested */
+               ssh2_pkt_addstring("none");    /* method */
+               ssh2_pkt_send();
+           } else if (key == NULL) {
+               c_write("Unable to load private key\r\n",28);
+               tried_pubkey_config = TRUE;
+               /* Send a spurious AUTH_NONE to return to the top. */
+               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(username);
+               ssh2_pkt_addstring("ssh-connection");   /* service requested */
+               ssh2_pkt_addstring("none");    /* method */
+               ssh2_pkt_send();
+           } else {
+               unsigned char *blob, *sigdata;
+               int blob_len, sigdata_len;
+
+               /*
+                * We have loaded the private key and the server
+                * has announced that it's willing to accept it.
+                * Hallelujah. Generate a signature and send it.
+                */
+               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(username);
+               ssh2_pkt_addstring("ssh-connection");   /* service requested */
+               ssh2_pkt_addstring("publickey");    /* method */
+               ssh2_pkt_addbool(TRUE);
+               ssh2_pkt_addstring(key->alg->name);
+               blob = key->alg->public_blob(key->data, &blob_len);
+               ssh2_pkt_addstring_start();
+               ssh2_pkt_addstring_data(blob, blob_len);
+               sfree(blob);
+
+               /*
+                * The data to be signed is:
+                * 
+                *   string  session-id
+                * 
+                * followed by everything so far placed in the
+                * outgoing packet.
+                */
+               sigdata_len = pktout.length - 5 + 4 + 20;
+               sigdata = smalloc(sigdata_len);
+               PUT_32BIT(sigdata, 20);
+               memcpy(sigdata+4, ssh2_session_id, 20);
+               memcpy(sigdata+24, pktout.data+5, pktout.length-5);
+               blob = key->alg->sign(key->data, sigdata, sigdata_len, &blob_len);
+               ssh2_pkt_addstring_start();
+               ssh2_pkt_addstring_data(blob, blob_len);
+               sfree(blob);
+               sfree(sigdata);
+
+               ssh2_pkt_send();
+           }
+       } else if (method == AUTH_PASSWORD) {
+           /*
+            * We send the password packet lumped tightly together with
+            * an SSH_MSG_IGNORE packet. The IGNORE packet contains a
+            * string long enough to make the total length of the two
+            * packets constant. This should ensure that a passive
+            * listener doing traffic analyis can't work out the length
+            * of the password.
+            *
+            * For this to work, we need an assumption about the
+            * maximum length of the password packet. I think 256 is
+            * pretty conservative. Anyone using a password longer than
+            * that probably doesn't have much to worry about from
+            * people who find out how long their password is!
+            */
+           ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+           ssh2_pkt_addstring(username);
+           ssh2_pkt_addstring("ssh-connection");   /* service requested */
+           ssh2_pkt_addstring("password");
+           ssh2_pkt_addbool(FALSE);
+           ssh2_pkt_addstring(password);
+           ssh2_pkt_defer();
+           /*
+            * We'll include a string that's an exact multiple of the
+            * cipher block size. If the cipher is NULL for some
+            * reason, we don't do this trick at all because we gain
+            * nothing by it.
+            */
+           if (cscipher) {
+               int i, j;
+               ssh2_pkt_init(SSH2_MSG_IGNORE);
+               ssh2_pkt_addstring_start();
+               for (i = deferred_len; i <= 256; i += cscipher->blksize) {
+                   for (j = 0; j < cscipher->blksize; j++) {
+                       char c = (char)random_byte();
+                       ssh2_pkt_addstring_data(&c, 1);
+                   }
+               }
+               ssh2_pkt_defer();
+           }
+           ssh2_pkt_defersend();
+       } else {
+           c_write("No supported authentication methods left to try!\r\n", 50);
+           logevent("No supported authentications offered. Disconnecting");
+           ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+           ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
+           ssh2_pkt_addstring("No supported authentication methods available");
+           ssh2_pkt_addstring("en");   /* language tag */
+           ssh2_pkt_send();
+           ssh_state = SSH_STATE_CLOSED;
+           crReturnV;
+       }
     }
 
     /*
diff --git a/ssh.h b/ssh.h
index aafee4e..ca9e550 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -34,14 +34,11 @@ struct RSAKey {
     Bignum modulus;
     Bignum exponent;
     Bignum private_exponent;
-#endif
-    char *comment;
-};
-
-struct RSAAux {
     Bignum p;
     Bignum q;
     Bignum iqmp;
+#endif
+    char *comment;
 };
 
 int makekey(unsigned char *data, struct RSAKey *result,
@@ -114,7 +111,7 @@ struct ssh2_cipher {
 
 struct ssh2_ciphers {
     int nciphers;
-    struct ssh2_cipher **list;
+    const struct ssh2_cipher *const *list;
 };
 
 struct ssh_mac {
@@ -141,11 +138,14 @@ struct ssh_signkey {
     void *(*newkey)(char *data, int len);
     void (*freekey)(void *key);
     char *(*fmtkey)(void *key);
+    unsigned char *(*public_blob)(void *key, int *len);
+    unsigned char *(*private_blob)(void *key, int *len);
+    void *(*createkey)(unsigned char *pub_blob, int pub_len,
+                      unsigned char *priv_blob, int priv_len);
     char *(*fingerprint)(void *key);
     int (*verifysig)(void *key, char *sig, int siglen,
                     char *data, int datalen);
-    int (*sign)(void *key, char *sig, int siglen,
-               char *data, int datalen);
+    unsigned char *(*sign)(void *key, char *data, int datalen, int *siglen);
     char *name;
     char *keytype;                     /* for host key cache */
 };
@@ -160,6 +160,26 @@ struct ssh_compress {
                      unsigned char **outblock, int *outlen);
 };
 
+struct ssh2_userkey {
+    const struct ssh_signkey *alg;     /* the key algorithm */
+    void *data;                               /* the key data */
+    char *comment;                    /* the key comment */
+};
+
+extern const struct ssh_cipher ssh_3des;
+extern const struct ssh_cipher ssh_des;
+extern const struct ssh_cipher ssh_blowfish_ssh1;
+extern const struct ssh2_ciphers ssh2_3des;
+extern const struct ssh2_ciphers ssh2_aes;
+extern const struct ssh2_ciphers ssh2_blowfish;
+extern const struct ssh_kex ssh_diffiehellman;
+extern const struct ssh_kex ssh_diffiehellman_gex;
+extern const struct ssh_signkey ssh_dss;
+extern const struct ssh_signkey ssh_rsa;
+extern const struct ssh_mac ssh_md5;
+extern const struct ssh_mac ssh_sha1;
+extern const struct ssh_mac ssh_sha1_buggy;
+
 #ifndef MSCRYPTOAPI
 void SHATransform(word32 *digest, word32 *data);
 #endif
@@ -203,25 +223,35 @@ void dh_cleanup(void);
 Bignum dh_create_e(void);
 Bignum dh_find_K(Bignum f);
 
-int loadrsakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
-               char *passphrase);
+int loadrsakey(char *filename, struct RSAKey *key, char *passphrase);
 int rsakey_encrypted(char *filename, char **comment);
 
-int saversakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
-               char *passphrase);
+int saversakey(char *filename, struct RSAKey *key, char *passphrase);
+
+void base64_encode_atom(unsigned char *data, int n, char *out);
+
+/* ssh2_load_userkey can return this as an error */
+extern struct ssh2_userkey ssh2_wrong_passphrase;
+#define SSH2_WRONG_PASSPHRASE (&ssh2_wrong_passphrase)
+
+int ssh2_userkey_encrypted(char *filename, char **comment);
+struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase);
+char *ssh2_userkey_loadpub(char *filename, char **algorithm, int *pub_blob_len);
+int ssh2_save_userkey(char *filename, struct ssh2_userkey *key, char *passphrase);
+
+int keyfile_version(char *filename);
 
-void des3_decrypt_pubkey(unsigned char *key,
-                         unsigned char *blk, int len);
-void des3_encrypt_pubkey(unsigned char *key,
-                         unsigned char *blk, int len);
+void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
+void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
+void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
+void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
 
 /*
  * For progress updates in the key generation utility.
  */
 typedef void (*progfn_t)(void *param, int phase, int progress);
 
-int rsa_generate(struct RSAKey *key, struct RSAAux *aux, 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);
 
index 0e7ddc1..68767cc 100644 (file)
--- a/sshaes.c
+++ b/sshaes.c
@@ -1023,7 +1023,21 @@ static void aes_ssh2_decrypt_blk(unsigned char *blk, int len) {
     aes_decrypt_cbc(blk, len, &scctx);
 }
 
-static struct ssh2_cipher ssh_aes128 = {
+void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len) {
+    AESContext ctx;
+    aes_setup(&ctx, 16, key, 32);
+    memset(ctx.iv, 0, sizeof(ctx.iv));
+    aes_encrypt_cbc(blk, len, &ctx);
+}
+
+void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len) {
+    AESContext ctx;
+    aes_setup(&ctx, 16, key, 32);
+    memset(ctx.iv, 0, sizeof(ctx.iv));
+    aes_decrypt_cbc(blk, len, &ctx);
+}
+
+static const struct ssh2_cipher ssh_aes128 = {
     aes_csiv, aes128_cskey,
     aes_sciv, aes128_sckey,
     aes_ssh2_encrypt_blk,
@@ -1032,7 +1046,7 @@ static struct ssh2_cipher ssh_aes128 = {
     16, 128
 };
 
-static struct ssh2_cipher ssh_aes192 = {
+static const struct ssh2_cipher ssh_aes192 = {
     aes_csiv, aes192_cskey,
     aes_sciv, aes192_sckey,
     aes_ssh2_encrypt_blk,
@@ -1041,7 +1055,7 @@ static struct ssh2_cipher ssh_aes192 = {
     16, 192
 };
 
-static struct ssh2_cipher ssh_aes256 = {
+static const struct ssh2_cipher ssh_aes256 = {
     aes_csiv, aes256_cskey,
     aes_sciv, aes256_sckey,
     aes_ssh2_encrypt_blk,
@@ -1050,7 +1064,7 @@ static struct ssh2_cipher ssh_aes256 = {
     16, 256
 };
 
-static struct ssh2_cipher ssh_rijndael128 = {
+static const struct ssh2_cipher ssh_rijndael128 = {
     aes_csiv, aes128_cskey,
     aes_sciv, aes128_sckey,
     aes_ssh2_encrypt_blk,
@@ -1059,7 +1073,7 @@ static struct ssh2_cipher ssh_rijndael128 = {
     16, 128
 };
 
-static struct ssh2_cipher ssh_rijndael192 = {
+static const struct ssh2_cipher ssh_rijndael192 = {
     aes_csiv, aes192_cskey,
     aes_sciv, aes192_sckey,
     aes_ssh2_encrypt_blk,
@@ -1068,7 +1082,7 @@ static struct ssh2_cipher ssh_rijndael192 = {
     16, 192
 };
 
-static struct ssh2_cipher ssh_rijndael256 = {
+static const struct ssh2_cipher ssh_rijndael256 = {
     aes_csiv, aes256_cskey,
     aes_sciv, aes256_sckey,
     aes_ssh2_encrypt_blk,
@@ -1077,7 +1091,7 @@ static struct ssh2_cipher ssh_rijndael256 = {
     16, 256
 };
 
-static struct ssh2_cipher ssh_rijndael_lysator = {
+static const struct ssh2_cipher ssh_rijndael_lysator = {
     aes_csiv, aes256_cskey,
     aes_sciv, aes256_sckey,
     aes_ssh2_encrypt_blk,
@@ -1086,7 +1100,7 @@ static struct ssh2_cipher ssh_rijndael_lysator = {
     16, 256
 };
 
-static struct ssh2_cipher *aes_list[] = {
+static const struct ssh2_cipher *const aes_list[] = {
     &ssh_aes256,
     &ssh_rijndael256,
     &ssh_rijndael_lysator,
@@ -1096,7 +1110,7 @@ static struct ssh2_cipher *aes_list[] = {
     &ssh_rijndael128,
 };
 
-struct ssh2_ciphers ssh2_aes = {
+const struct ssh2_ciphers ssh2_aes = {
     sizeof(aes_list) / sizeof(*aes_list),
     aes_list
 };
index 99d8373..f3c49b8 100644 (file)
@@ -507,14 +507,14 @@ static void blowfish_ssh2_decrypt_blk(unsigned char *blk, int len)
     blowfish_msb_decrypt_cbc(blk, len, &dctx);
 }
 
-struct ssh_cipher ssh_blowfish_ssh1 = {
+const struct ssh_cipher ssh_blowfish_ssh1 = {
     blowfish_sesskey,
     blowfish_ssh1_encrypt_blk,
     blowfish_ssh1_decrypt_blk,
     8
 };
 
-static struct ssh2_cipher ssh_blowfish_ssh2 = {
+static const struct ssh2_cipher ssh_blowfish_ssh2 = {
     blowfish_csiv, blowfish_cskey,
     blowfish_sciv, blowfish_sckey,
     blowfish_ssh2_encrypt_blk,
@@ -523,11 +523,11 @@ static struct ssh2_cipher ssh_blowfish_ssh2 = {
     8, 128
 };
 
-static struct ssh2_cipher *blowfish_list[] = {
+static const struct ssh2_cipher *const blowfish_list[] = {
     &ssh_blowfish_ssh2
 };
 
-struct ssh2_ciphers ssh2_blowfish = {
+const struct ssh2_ciphers ssh2_blowfish = {
     sizeof(blowfish_list) / sizeof(*blowfish_list),
     blowfish_list
 };
index 491c906..1665b3d 100644 (file)
--- a/sshdes.c
+++ b/sshdes.c
@@ -790,7 +790,7 @@ void des3_encrypt_pubkey(unsigned char *key,
     des_3cbc_encrypt(blk, blk, len, ourkeys);
 }
 
-static struct ssh2_cipher ssh_3des_ssh2 = {
+static const struct ssh2_cipher ssh_3des_ssh2 = {
     des3_csiv, des3_cskey,
     des3_sciv, des3_sckey,
     des3_ssh2_encrypt_blk,
@@ -799,16 +799,16 @@ static struct ssh2_cipher ssh_3des_ssh2 = {
     8, 168
 };
 
-static struct ssh2_cipher *des3_list[] = {
+static const struct ssh2_cipher *const des3_list[] = {
     &ssh_3des_ssh2
 };
 
-struct ssh2_ciphers ssh2_3des = {
+const struct ssh2_ciphers ssh2_3des = {
     sizeof(des3_list) / sizeof(*des3_list),
     des3_list
 };
 
-struct ssh_cipher ssh_3des = {
+const struct ssh_cipher ssh_3des = {
     des3_sesskey,
     des3_encrypt_blk,
     des3_decrypt_blk,
@@ -829,7 +829,7 @@ static void des_decrypt_blk(unsigned char *blk, int len) {
     des_cbc_decrypt(blk, blk, len, cskeys);
 }
 
-struct ssh_cipher ssh_des = {
+const struct ssh_cipher ssh_des = {
     des_sesskey,
     des_encrypt_blk,
     des_decrypt_blk,
diff --git a/sshdh.c b/sshdh.c
index 18749ec..f1d709f 100644 (file)
--- a/sshdh.c
+++ b/sshdh.c
@@ -1,10 +1,10 @@
 #include "ssh.h"
 
-struct ssh_kex ssh_diffiehellman = {
+const struct ssh_kex ssh_diffiehellman = {
     "diffie-hellman-group1-sha1"
 };
 
-struct ssh_kex ssh_diffiehellman_gex = {
+const struct ssh_kex ssh_diffiehellman_gex = {
     "diffie-hellman-group-exchange-sha1"
 };
 
index 1704f71..c561f5a 100644 (file)
--- a/sshdss.c
+++ b/sshdss.c
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <assert.h>
 
 #include "ssh.h"
 
@@ -272,15 +273,59 @@ static int dss_verifysig(void *key, char *sig, int siglen,
     return ret;
 }
 
-int dss_sign(void *key, char *sig, int siglen,
-            char *data, int datalen) {
-    return 0;                         /* do nothing */
+static unsigned char *dss_public_blob(void *key, int *len) {
+    struct dss_key *dss = (struct dss_key *)key;
+    int plen, qlen, glen, ylen, bloblen;
+    int i;
+    unsigned char *blob, *p;
+
+    plen = (ssh1_bignum_bitcount(dss->p)+8)/8;
+    qlen = (ssh1_bignum_bitcount(dss->q)+8)/8;
+    glen = (ssh1_bignum_bitcount(dss->g)+8)/8;
+    ylen = (ssh1_bignum_bitcount(dss->y)+8)/8;
+
+    /*
+     * string "ssh-dss", mpint p, mpint q, mpint g, mpint y. Total
+     * 27 + sum of lengths. (five length fields, 20+7=27).
+     */
+    bloblen = 27+plen+qlen+glen+ylen;
+    blob = smalloc(bloblen);
+    p = blob;
+    PUT_32BIT(p, 7); p += 4;
+    memcpy(p, "ssh-dss", 7); p += 7;
+    PUT_32BIT(p, plen); p += 4;
+    for (i = plen; i-- ;) *p++ = bignum_byte(dss->p, i);
+    PUT_32BIT(p, qlen); p += 4;
+    for (i = qlen; i-- ;) *p++ = bignum_byte(dss->q, i);
+    PUT_32BIT(p, glen); p += 4;
+    for (i = glen; i-- ;) *p++ = bignum_byte(dss->g, i);
+    PUT_32BIT(p, ylen); p += 4;
+    for (i = ylen; i-- ;) *p++ = bignum_byte(dss->y, i);
+    assert(p == blob + bloblen);
+    *len = bloblen;
+    return blob;
+}
+
+static unsigned char *dss_private_blob(void *key, int *len) {
+    return NULL;                      /* can't handle DSS private keys */
+}
+
+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 */
+}
+
+unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) {
+    return NULL;                      /* can't handle DSS private keys */
 }
 
-struct ssh_signkey ssh_dss = {
+const struct ssh_signkey ssh_dss = {
     dss_newkey,
     dss_freekey,
     dss_fmtkey,
+    dss_public_blob,
+    dss_private_blob,
+    dss_createkey,
     dss_fingerprint,
     dss_verifysig,
     dss_sign,
index a84e6ad..2b0d5f5 100644 (file)
--- a/sshmd5.c
+++ b/sshmd5.c
@@ -259,7 +259,7 @@ static int md5_verify(unsigned char *blk, int len, unsigned long seq) {
     return !memcmp(correct, blk+len, 16);
 }
 
-struct ssh_mac ssh_md5 = {
+const struct ssh_mac ssh_md5 = {
     md5_cskey, md5_sckey,
     md5_generate,
     md5_verify,
index 377be3a..ce237b9 100644 (file)
--- a/sshpubk.c
+++ b/sshpubk.c
@@ -7,6 +7,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <assert.h>
 
 #include "ssh.h"
 
@@ -30,7 +31,7 @@
                           (x)=='+' ? 62 : \
                           (x)=='/' ? 63 : 0 )
 
-static int loadrsakey_main(FILE *fp, struct RSAKey *key, struct RSAAux *aux,
+static int loadrsakey_main(FILE *fp, struct RSAKey *key,
                            char **commentptr, char *passphrase) {
     unsigned char buf[16384];
     unsigned char keybuf[16];
@@ -117,14 +118,12 @@ static int loadrsakey_main(FILE *fp, struct RSAKey *key, struct RSAAux *aux,
      */
     i += makeprivate(buf+i, key);
     if (len-i < 0) goto end;
-    if (aux) {
-        i += ssh1_read_bignum(buf+i, &aux->iqmp);
-        if (len-i < 0) goto end;
-        i += ssh1_read_bignum(buf+i, &aux->q);
-        if (len-i < 0) goto end;
-        i += ssh1_read_bignum(buf+i, &aux->p);
-        if (len-i < 0) goto end;
-    }
+    i += ssh1_read_bignum(buf+i, &key->iqmp);
+    if (len-i < 0) goto end;
+    i += ssh1_read_bignum(buf+i, &key->q);
+    if (len-i < 0) goto end;
+    i += ssh1_read_bignum(buf+i, &key->p);
+    if (len-i < 0) goto end;
 
     ret = 1;
     end:
@@ -132,8 +131,7 @@ static int loadrsakey_main(FILE *fp, struct RSAKey *key, struct RSAAux *aux,
     return ret;
 }
 
-int loadrsakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
-               char *passphrase) {
+int loadrsakey(char *filename, struct RSAKey *key, char *passphrase) {
     FILE *fp;
     unsigned char buf[64];
 
@@ -147,7 +145,7 @@ int loadrsakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
      */
     if (fgets(buf, sizeof(buf), fp) &&
         !strcmp(buf, rsa_signature)) {
-        return loadrsakey_main(fp, key, aux, NULL, passphrase);
+        return loadrsakey_main(fp, key, NULL, passphrase);
     }
 
     /*
@@ -175,7 +173,7 @@ int rsakey_encrypted(char *filename, char **comment) {
      */
     if (fgets(buf, sizeof(buf), fp) &&
         !strcmp(buf, rsa_signature)) {
-        return loadrsakey_main(fp, NULL, NULL, comment, NULL);
+        return loadrsakey_main(fp, NULL, comment, NULL);
     }
     fclose(fp);
     return 0;                          /* wasn't the right kind of file */
@@ -184,8 +182,7 @@ int rsakey_encrypted(char *filename, char **comment) {
 /*
  * Save an RSA key file. Return nonzero on success.
  */
-int saversakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
-               char *passphrase) {
+int saversakey(char *filename, struct RSAKey *key, char *passphrase) {
     unsigned char buf[16384];
     unsigned char keybuf[16];
     struct MD5Context md5c;
@@ -243,9 +240,9 @@ int saversakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
      * q, then p.
      */
     p += ssh1_write_bignum(p, key->private_exponent);
-    p += ssh1_write_bignum(p, aux->iqmp);
-    p += ssh1_write_bignum(p, aux->q);
-    p += ssh1_write_bignum(p, aux->p);
+    p += ssh1_write_bignum(p, key->iqmp);
+    p += ssh1_write_bignum(p, key->q);
+    p += ssh1_write_bignum(p, key->p);
 
     /*
      * Now write zeros until the encrypted portion is a multiple of
@@ -276,3 +273,619 @@ int saversakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
     } else
         return 0;
 }
+
+/* ----------------------------------------------------------------------
+ * SSH2 private key load/store functions.
+ */
+
+/*
+ * 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.)
+ * 
+ * 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)
+ * 
+ * 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.
+ * 
+ * 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
+ * 
+ * where the sequence-number increases from zero. As many of these
+ * hashes are used as necessary.
+ */
+
+static int read_header(FILE *fp, char *header) {
+    int len = 39;
+    int c;
+
+    while (len > 0) {
+       c = fgetc(fp);
+       if (c == '\n' || c == '\r' || c == EOF)
+           return 0;                  /* failure */
+       if (c == ':') {
+           c = fgetc(fp);
+           if (c != ' ')
+               return 0;
+           *header = '\0';
+           return 1;                  /* success! */
+       }
+       if (len == 0)
+           return 0;                  /* failure */
+       *header++ = c;
+       len--;
+    }
+    return 0;                         /* failure */
+}
+
+static char *read_body(FILE *fp) {
+    char *text;
+    int len;
+    int size;
+    int c;
+
+    size = 128;
+    text = smalloc(size);
+    len = 0;
+    text[len] = '\0';
+
+    while (1) {
+       c = fgetc(fp);
+       if (c == '\r' || c == '\n') {
+           c = fgetc(fp);
+           if (c != '\r' && c != '\n' && c != EOF)
+               ungetc(c, fp);
+           return text;
+       }
+       if (c == EOF) {
+           sfree(text);
+           return NULL;
+       }
+       if (len + 1 > size) {
+           size += 128;
+           text = srealloc(text, size);
+       }
+       text[len++] = c;
+       text[len] = '\0';
+    }
+}
+
+int base64_decode_atom(char *atom, unsigned char *out) {
+    int vals[4];
+    int i, v, len;
+    unsigned word;
+    char c;
+    
+    for (i = 0; i < 4; i++) {
+       c = atom[i];
+       if (c >= 'A' && c <= 'Z')
+           v = c - 'A';
+       else if (c >= 'a' && c <= 'z')
+           v = c - 'a' + 26;
+       else if (c >= '0' && c <= '9')
+           v = c - '0' + 52;
+       else if (c == '+')
+           v = 62;
+       else if (c == '/')
+           v = 63;
+       else if (c == '=')
+           v = -1;
+       else
+           return 0;                  /* invalid atom */
+       vals[i] = v;
+    }
+
+    if (vals[0] == -1 || vals[1] == -1)
+       return 0;
+    if (vals[2] == -1 && vals[3] != -1)
+       return 0;
+
+    if (vals[3] != -1)
+       len = 3;
+    else if (vals[2] != -1)
+       len = 2;
+    else
+       len = 1;
+
+    word = ((vals[0] << 18) |
+           (vals[1] << 12) |
+           ((vals[2] & 0x3F) << 6) |
+           (vals[3] & 0x3F));
+    out[0] = (word >> 16) & 0xFF;
+    if (len > 1)
+       out[1] = (word >> 8) & 0xFF;
+    if (len > 2)
+       out[2] = word & 0xFF;
+    return len;
+}
+
+static char *read_blob(FILE *fp, int nlines, int *bloblen) {
+    unsigned char *blob;
+    char *line;
+    int linelen, len;
+    int i, j, k;
+
+    /* We expect at most 64 base64 characters, ie 48 real bytes, per line. */
+    blob = smalloc(48 * nlines);
+    len = 0;
+    for (i = 0; i < nlines; i++) {
+       line = read_body(fp);
+       if (!line) {
+           sfree(blob);
+           return NULL;
+       }
+       linelen = strlen(line);
+       if (linelen % 4 != 0 || linelen > 64) {
+           sfree(blob);
+           sfree(line);
+           return NULL;
+       }
+       for (j = 0; j < linelen; j += 4) {
+           k = base64_decode_atom(line+j, blob+len);
+           if (!k) {
+               sfree(line);
+               sfree(blob);
+               return NULL;
+           }
+           len += k;
+       }
+       sfree(line);
+    }
+    *bloblen = len;
+    return blob;
+}
+
+/*
+ * Magic error return value for when the passphrase is wrong.
+ */
+struct ssh2_userkey ssh2_wrong_passphrase = {
+    NULL, NULL, NULL
+};
+
+struct ssh2_userkey *ssh2_load_userkey(char *filename, char *passphrase) {
+    FILE *fp;
+    char header[40], *b, *comment, *hash;
+    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;
+
+    ret = NULL;                               /* return NULL for most errors */
+    comment = hash = NULL;
+    public_blob = private_blob = NULL;
+
+    fp = fopen(filename, "rb");
+    if (!fp)
+       goto error;
+
+    /* Read the first header line which contains the key type. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "PuTTY-User-Key-File-1"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    /* Select key algorithm structure. Currently only ssh-rsa. */
+    if (!strcmp(b, "ssh-rsa"))
+       alg = &ssh_rsa;
+    else {
+       sfree(b);
+       goto error;
+    }
+    sfree(b);
+    
+    /* Read the Encryption header line. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Encryption"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    if (!strcmp(b, "aes256-cbc")) {
+       cipher = 1; cipherblk = 16;
+    } else if (!strcmp(b, "none")) {
+       cipher = 0; cipherblk = 1;
+    } else {
+       sfree(b);
+       goto error;
+    }
+    sfree(b);
+
+    /* Read the Comment header line. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Comment"))
+       goto error;
+    if ((comment = read_body(fp)) == NULL)
+       goto error;
+
+    /* Read the Public-Lines header line and the public blob. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Public-Lines"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    i = atoi(b);
+    sfree(b);
+    if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
+       goto error;
+
+    /* Read the Private-Lines header line and the Private blob. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Private-Lines"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    i = atoi(b);
+    sfree(b);
+    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"))
+       goto error;
+    if ((hash = read_body(fp)) == NULL)
+       goto error;
+
+    fclose(fp);
+    fp = NULL;
+
+    /*
+     * Decrypt the private blob.
+     */
+    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);
+       SHA_Final(&s, key+0);
+       SHA_Init(&s);
+       SHA_Bytes(&s, "\0\0\0\1", 4);
+       SHA_Bytes(&s, passphrase, passlen);
+       SHA_Final(&s, key+20);
+       aes256_decrypt_pubkey(key, private_blob, private_blob_len);
+    }
+
+    /*
+     * Verify the private hash.
+     */
+    {
+       char realhash[41];
+       unsigned char binary[20];
+       SHA_State s;
+
+       SHA_Simple(private_blob, private_blob_len, binary);
+       for (i = 0; i < 20; i++)
+           sprintf(realhash+2*i, "%02x", binary[i]);
+
+       if (strcmp(hash, realhash)) {
+           /* An incorrect hash is an unconditional Error if the key is
+            * unencrypted. Otherwise, it means Wrong Passphrase. */
+           ret = cipher ? SSH2_WRONG_PASSPHRASE : NULL;
+           goto error;
+       }
+    }
+    sfree(hash);
+
+    /*
+     * Create and return the key.
+     */
+    ret = smalloc(sizeof(struct ssh2_userkey));
+    ret->alg = alg;
+    ret->comment = comment;
+    ret->data = alg->createkey(public_blob, public_blob_len,
+                              private_blob, private_blob_len);
+    sfree(public_blob);
+    sfree(private_blob);
+    return ret;
+
+    /*
+     * Error processing.
+     */
+    error:
+    if (fp) fclose(fp);
+    if (comment) sfree(comment);
+    if (hash) sfree(hash);
+    if (public_blob) sfree(public_blob);
+    if (private_blob) sfree(private_blob);
+    return ret;
+}
+
+char *ssh2_userkey_loadpub(char *filename, char **algorithm, int *pub_blob_len) {
+    FILE *fp;
+    char header[40], *b;
+    const struct ssh_signkey *alg;
+    int cipher, cipherblk;
+    unsigned char *public_blob;
+    int public_blob_len;
+    int i;
+
+    public_blob = NULL;
+
+    fp = fopen(filename, "rb");
+    if (!fp)
+       goto error;
+
+    /* Read the first header line which contains the key type. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "PuTTY-User-Key-File-1"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    /* Select key algorithm structure. Currently only ssh-rsa. */
+    if (!strcmp(b, "ssh-rsa"))
+       alg = &ssh_rsa;
+    else {
+       sfree(b);
+       goto error;
+    }
+    sfree(b);
+    
+    /* Read the Encryption header line. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Encryption"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    sfree(b);                         /* we don't care */
+
+    /* Read the Comment header line. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Comment"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    sfree(b);                         /* we don't care */
+
+    /* Read the Public-Lines header line and the public blob. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Public-Lines"))
+       goto error;
+    if ((b = read_body(fp)) == NULL)
+       goto error;
+    i = atoi(b);
+    sfree(b);
+    if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
+       goto error;
+
+    fclose(fp);
+    *pub_blob_len = public_blob_len;
+    *algorithm = alg->name;
+    return public_blob;
+
+    /*
+     * Error processing.
+     */
+    error:
+    if (fp) fclose(fp);
+    if (public_blob) sfree(public_blob);
+    return NULL;
+}
+
+int ssh2_userkey_encrypted(char *filename, char **commentptr) {
+    FILE *fp;
+    char header[40], *b, *comment;
+    int ret;
+
+    if (commentptr) *commentptr = NULL;
+
+    fp = fopen(filename, "rb");
+    if (!fp)
+       return 0;
+    if (!read_header(fp, header) || 0!=strcmp(header, "PuTTY-User-Key-File-1")) {
+       fclose(fp); return 0;
+    }
+    if ((b = read_body(fp)) == NULL) {
+       fclose(fp); return 0;
+    }
+    sfree(b);                         /* we don't care about key type here */
+    /* Read the Encryption header line. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Encryption")) {
+       fclose(fp); return 0;
+    }
+    if ((b = read_body(fp)) == NULL) {
+       fclose(fp); return 0;
+    }
+
+    /* Read the Comment header line. */
+    if (!read_header(fp, header) || 0!=strcmp(header, "Comment")) {
+       fclose(fp); sfree(b); return 1;
+    }
+    if ((comment = read_body(fp)) == NULL) {
+       fclose(fp); sfree(b); return 1;
+    }
+
+    if (commentptr) *commentptr = comment;
+
+    fclose(fp);
+    if (!strcmp(b, "aes256-cbc"))
+       ret = 1;
+    else
+       ret = 0;
+    sfree(b);
+    return ret;
+}
+
+int base64_lines(int datalen) {
+    /* When encoding, we use 64 chars/line, which equals 48 real chars. */
+    return (datalen+47) / 48;
+}
+
+void base64_encode_atom(unsigned char *data, int n, char *out) {
+    static const char base64_chars[] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    unsigned word;
+
+    word = data[0] << 16;
+    if (n > 1)
+       word |= data[1] << 8;
+    if (n > 2)
+       word |= data[2];
+    out[0] = base64_chars[(word >> 18) & 0x3F];
+    out[1] = base64_chars[(word >> 12) & 0x3F];
+    if (n > 1)
+       out[2] = base64_chars[(word >> 6) & 0x3F];
+    else
+       out[2] = '=';
+    if (n > 2)
+       out[3] = base64_chars[word & 0x3F];
+    else
+       out[3] = '=';
+}
+
+void base64_encode(FILE *fp, unsigned char *data, int datalen) {
+    int linelen = 0;
+    char out[4];
+    int n;
+
+    while (datalen > 0) {
+       if (linelen >= 64) {
+           linelen = 0;
+           fputc('\n', fp);
+       }
+       n = (datalen < 3 ? datalen : 3);
+       base64_encode_atom(data, n, out);
+       data += n;
+       datalen -= n;
+       fwrite(out, 1, 4, fp);
+       linelen += 4;
+    }
+    fputc('\n', fp);
+}
+
+int ssh2_save_userkey(char *filename, struct ssh2_userkey *key, char *passphrase) {
+    FILE *fp;
+    unsigned char *pub_blob, *priv_blob, *priv_blob_encrypted;
+    int pub_blob_len, priv_blob_len, priv_encrypted_len;
+    int passlen;
+    int cipherblk;
+    int i;
+    char *cipherstr;
+    unsigned char priv_hash[20];
+
+    /*
+     * Fetch the key component blobs.
+     */
+    pub_blob = key->alg->public_blob(key->data, &pub_blob_len);
+    priv_blob = key->alg->private_blob(key->data, &priv_blob_len);
+    if (!pub_blob || !priv_blob) {
+       sfree(pub_blob);
+       sfree(priv_blob);
+       return 0;
+    }
+
+    /*
+     * Determine encryption details, and encrypt the private blob.
+     */
+    if (passphrase) {
+       cipherstr = "aes256-cbc";
+       cipherblk = 16;
+    } else {
+       cipherstr = "none";
+       cipherblk = 1;
+    }
+    priv_encrypted_len = priv_blob_len + cipherblk - 1;
+    priv_encrypted_len -= priv_encrypted_len % cipherblk;
+    priv_blob_encrypted = smalloc(priv_encrypted_len);
+    memset(priv_blob_encrypted, 0, priv_encrypted_len);
+    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);
+    assert(priv_encrypted_len - priv_blob_len < 20);
+    memcpy(priv_blob_encrypted + priv_blob_len, priv_hash,
+          priv_encrypted_len - priv_blob_len);
+
+    /* Now create the _real_ private hash. */
+    SHA_Simple(priv_blob_encrypted, priv_encrypted_len, priv_hash);
+
+    if (passphrase) {
+       char key[40];
+       SHA_State s;
+
+       passlen = strlen(passphrase);
+
+       SHA_Init(&s);
+       SHA_Bytes(&s, "\0\0\0\0", 4);
+       SHA_Bytes(&s, passphrase, passlen);
+       SHA_Final(&s, key+0);
+       SHA_Init(&s);
+       SHA_Bytes(&s, "\0\0\0\1", 4);
+       SHA_Bytes(&s, passphrase, passlen);
+       SHA_Final(&s, key+20);
+       aes256_encrypt_pubkey(key, priv_blob_encrypted, priv_encrypted_len);
+    }
+
+    fp = fopen(filename, "w");
+    if (!fp)
+       return 0;
+    fprintf(fp, "PuTTY-User-Key-File-1: %s\n", key->alg->name);
+    fprintf(fp, "Encryption: %s\n", cipherstr);
+    fprintf(fp, "Comment: %s\n", key->comment);
+    fprintf(fp, "Public-Lines: %d\n", base64_lines(pub_blob_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: ");
+    for (i = 0; i < 20; i++)
+       fprintf(fp, "%02x", priv_hash[i]);
+    fprintf(fp, "\n");
+    fclose(fp);
+    return 1;
+}
+
+/* ----------------------------------------------------------------------
+ * A function to determine which version of SSH to try on a private
+ * key file. Returns 0 on failure, 1 or 2 on success.
+ */
+int keyfile_version(char *filename) {
+    FILE *fp;
+    int i;
+
+    fp = fopen(filename, "r");
+    if (!fp)
+       return 0;
+    i = fgetc(fp);
+    fclose(fp);
+    if (i == 'S')
+       return 1;                      /* "SSH PRIVATE KEY FORMAT" etc */
+    if (i == 'P')                     /* "PuTTY-User-Key-File" etc */
+       return 2;
+    return 0;                         /* unrecognised or EOF */
+}
index 6343944..c933862 100644 (file)
--- a/sshrsa.c
+++ b/sshrsa.c
@@ -8,6 +8,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
+#include <windows.h> // FIXME
+#include "putty.h" // FIXME
 
 #include "ssh.h"
 
@@ -234,6 +237,78 @@ static char *rsa2_fmtkey(void *key) {
     return p;
 }
 
+static unsigned char *rsa2_public_blob(void *key, int *len) {
+    struct RSAKey *rsa = (struct RSAKey *)key;
+    int elen, mlen, bloblen;
+    int i;
+    unsigned char *blob, *p;
+
+    elen = (ssh1_bignum_bitcount(rsa->exponent)+8)/8;
+    mlen = (ssh1_bignum_bitcount(rsa->modulus)+8)/8;
+
+    /*
+     * string "ssh-rsa", mpint exp, mpint mod. Total 19+elen+mlen.
+     * (three length fields, 12+7=19).
+     */
+    bloblen = 19+elen+mlen;
+    blob = smalloc(bloblen);
+    p = blob;
+    PUT_32BIT(p, 7); p += 4;
+    memcpy(p, "ssh-rsa", 7); p += 7;
+    PUT_32BIT(p, elen); p += 4;
+    for (i = elen; i-- ;) *p++ = bignum_byte(rsa->exponent, i);
+    PUT_32BIT(p, mlen); p += 4;
+    for (i = mlen; i-- ;) *p++ = bignum_byte(rsa->modulus, i);
+    assert(p == blob + bloblen);
+    *len = bloblen;
+    return blob;
+}
+
+static unsigned char *rsa2_private_blob(void *key, int *len) {
+    struct RSAKey *rsa = (struct RSAKey *)key;
+    int dlen, plen, qlen, ulen, bloblen;
+    int i;
+    unsigned char *blob, *p;
+
+    dlen = (ssh1_bignum_bitcount(rsa->private_exponent)+8)/8;
+    plen = (ssh1_bignum_bitcount(rsa->p)+8)/8;
+    qlen = (ssh1_bignum_bitcount(rsa->q)+8)/8;
+    ulen = (ssh1_bignum_bitcount(rsa->iqmp)+8)/8;
+
+    /*
+     * mpint private_exp, mpint p, mpint q, mpint iqmp. Total 16 +
+     * sum of lengths.
+     */
+    bloblen = 16+dlen+plen+qlen+ulen;
+    blob = smalloc(bloblen);
+    p = blob;
+    PUT_32BIT(p, dlen); p += 4;
+    for (i = dlen; i-- ;) *p++ = bignum_byte(rsa->private_exponent, i);
+    PUT_32BIT(p, plen); p += 4;
+    for (i = plen; i-- ;) *p++ = bignum_byte(rsa->p, i);
+    PUT_32BIT(p, qlen); p += 4;
+    for (i = qlen; i-- ;) *p++ = bignum_byte(rsa->q, i);
+    PUT_32BIT(p, ulen); p += 4;
+    for (i = ulen; i-- ;) *p++ = bignum_byte(rsa->iqmp, i);
+    assert(p == blob + bloblen);
+    *len = bloblen;
+    return blob;
+}
+
+static void *rsa2_createkey(unsigned char *pub_blob, int pub_len,
+                           unsigned char *priv_blob, int priv_len) {
+    struct RSAKey *rsa;
+    char *pb = (char *)priv_blob;
+    
+    rsa = rsa2_newkey((char *)pub_blob, pub_len);
+    rsa->private_exponent = getmp(&pb, &priv_len);
+    rsa->p = getmp(&pb, &priv_len);
+    rsa->q = getmp(&pb, &priv_len);
+    rsa->iqmp = getmp(&pb, &priv_len);
+
+    return rsa;
+}
+
 static char *rsa2_fingerprint(void *key) {
     struct RSAKey *rsa = (struct RSAKey *)key;
     struct MD5Context md5c;
@@ -333,15 +408,53 @@ static int rsa2_verifysig(void *key, char *sig, int siglen,
     return ret;
 }
 
-int rsa2_sign(void *key, char *sig, int siglen,
-            char *data, int datalen) {
-    return 0;                         /* FIXME */
+unsigned char *rsa2_sign(void *key, char *data, int datalen, int *siglen) {
+    struct RSAKey *rsa = (struct RSAKey *)key;
+    unsigned char *bytes;
+    int nbytes;
+    unsigned char hash[20];
+    Bignum in, out;
+    int i, j;
+
+    SHA_Simple(data, datalen, hash);
+
+    nbytes = (ssh1_bignum_bitcount(rsa->modulus)-1) / 8;
+    bytes = smalloc(nbytes);
+
+    bytes[0] = 1;
+    for (i = 1; i < nbytes-20-sizeof(asn1_weird_stuff); i++)
+       bytes[i] = 0xFF;
+    for (i = nbytes-20-sizeof(asn1_weird_stuff), j=0; i < nbytes-20; i++,j++)
+       bytes[i] = asn1_weird_stuff[j];
+    for (i = nbytes-20, j=0; i < nbytes; i++,j++)
+       bytes[i] = hash[j];
+
+    in = bignum_from_bytes(bytes, nbytes);
+    sfree(bytes);
+
+    out = modpow(in, rsa->private_exponent, rsa->modulus);
+    freebn(in);
+
+    nbytes = (ssh1_bignum_bitcount(out)+7)/8;
+    bytes = smalloc(4+7+4+nbytes);
+    PUT_32BIT(bytes, 7);
+    memcpy(bytes+4, "ssh-rsa", 7);
+    PUT_32BIT(bytes+4+7, nbytes);
+    for (i = 0; i < nbytes; i++)
+       bytes[4+7+4+i] = bignum_byte(out, nbytes-1-i);
+    freebn(out);
+
+    *siglen = 4+7+4+nbytes;
+    return bytes;
 }
 
-struct ssh_signkey ssh_rsa = {
+const struct ssh_signkey ssh_rsa = {
     rsa2_newkey,
     rsa2_freekey,
     rsa2_fmtkey,
+    rsa2_public_blob,
+    rsa2_private_blob,
+    rsa2_createkey,
     rsa2_fingerprint,
     rsa2_verifysig,
     rsa2_sign,
index f8e7651..88a3d83 100644 (file)
--- a/sshrsag.c
+++ b/sshrsag.c
@@ -23,8 +23,7 @@ static void diagbn(char *prefix, Bignum md) {
 }
 #endif
 
-int rsa_generate(struct RSAKey *key, struct RSAAux *aux, int bits,
-                 progfn_t pfn, void *pfnparam) {
+int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn, void *pfnparam) {
     Bignum pm1, qm1, phi_n;
 
     /*
@@ -71,16 +70,16 @@ int rsa_generate(struct RSAKey *key, struct RSAAux *aux, int bits,
      * general that's slightly more fiddly to arrange. By choosing
      * a prime e, we can simplify the criterion.)
      */
-    aux->p = primegen(bits/2, RSA_EXPONENT, 1, 1, pfn, pfnparam);
-    aux->q = primegen(bits - bits/2, RSA_EXPONENT, 1, 2, pfn, pfnparam);
+    key->p = primegen(bits/2, RSA_EXPONENT, 1, 1, pfn, pfnparam);
+    key->q = primegen(bits - bits/2, RSA_EXPONENT, 1, 2, pfn, pfnparam);
 
     /*
      * Ensure p > q, by swapping them if not.
      */
-    if (bignum_cmp(aux->p, aux->q) < 0) {
-        Bignum t = aux->p;
-        aux->p = aux->q;
-        aux->q = t;
+    if (bignum_cmp(key->p, key->q) < 0) {
+        Bignum t = key->p;
+        key->p = key->q;
+        key->q = t;
     }
 
     /*
@@ -89,11 +88,11 @@ int rsa_generate(struct RSAKey *key, struct RSAAux *aux, int bits,
      * and (q^-1 mod p).
      */
     pfn(pfnparam, 3, 1);
-    key->modulus = bigmul(aux->p, aux->q);
+    key->modulus = bigmul(key->p, key->q);
     pfn(pfnparam, 3, 2);
-    pm1 = copybn(aux->p);
+    pm1 = copybn(key->p);
     decbn(pm1);
-    qm1 = copybn(aux->q);
+    qm1 = copybn(key->q);
     decbn(qm1);
     phi_n = bigmul(pm1, qm1);
     pfn(pfnparam, 3, 3);
@@ -101,7 +100,7 @@ int rsa_generate(struct RSAKey *key, struct RSAAux *aux, int bits,
     freebn(qm1);
     key->private_exponent = modinv(key->exponent, phi_n);
     pfn(pfnparam, 3, 4);
-    aux->iqmp = modinv(aux->q, aux->p);
+    key->iqmp = modinv(key->q, key->p);
     pfn(pfnparam, 3, 5);
 
     /*
index 48cabb5..2a07cc3 100644 (file)
--- a/sshsha.c
+++ b/sshsha.c
@@ -237,7 +237,7 @@ static int sha1_verify(unsigned char *blk, int len, unsigned long seq) {
     return !memcmp(correct, blk+len, 20);
 }
 
-struct ssh_mac ssh_sha1 = {
+const struct ssh_mac ssh_sha1 = {
     sha1_cskey, sha1_sckey,
     sha1_generate,
     sha1_verify,
@@ -245,7 +245,7 @@ struct ssh_mac ssh_sha1 = {
     20
 };
 
-struct ssh_mac ssh_sha1_buggy = {
+const struct ssh_mac ssh_sha1_buggy = {
     sha1_cskey_buggy, sha1_sckey_buggy,
     sha1_generate,
     sha1_verify,