[BACK]Return to kex.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/kex.c between version 1.12 and 1.12.2.5

version 1.12, 2000/10/11 20:27:23 version 1.12.2.5, 2001/09/27 00:15:42
Line 1 
Line 1 
 /*  /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.   * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
Line 25 
Line 25 
 #include "includes.h"  #include "includes.h"
 RCSID("$OpenBSD$");  RCSID("$OpenBSD$");
   
 #include "ssh.h"  #include <openssl/crypto.h>
   
 #include "ssh2.h"  #include "ssh2.h"
 #include "xmalloc.h"  #include "xmalloc.h"
 #include "buffer.h"  #include "buffer.h"
 #include "bufaux.h"  #include "bufaux.h"
 #include "packet.h"  #include "packet.h"
 #include "compat.h"  #include "compat.h"
   #include "cipher.h"
 #include <openssl/bn.h>  
 #include <openssl/dh.h>  
   
 #include <openssl/crypto.h>  
 #include <openssl/bio.h>  
 #include <openssl/bn.h>  
 #include <openssl/dh.h>  
 #include <openssl/pem.h>  
   
 #include "kex.h"  #include "kex.h"
   #include "key.h"
   #include "log.h"
   #include "mac.h"
   #include "match.h"
   #include "dispatch.h"
   
 #define KEX_COOKIE_LEN  16  #define KEX_COOKIE_LEN  16
   
 Buffer *  /* prototype */
 kex_init(char *myproposal[PROPOSAL_MAX])  static void kex_kexinit_finish(Kex *);
   static void kex_choose_conf(Kex *);
   
   /* put algorithm proposal into buffer */
   static void
   kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
 {  {
         int first_kex_packet_follows = 0;  
         unsigned char cookie[KEX_COOKIE_LEN];  
         u_int32_t rand = 0;          u_int32_t rand = 0;
         int i;          int i;
         Buffer *ki = xmalloc(sizeof(*ki));  
           buffer_clear(b);
         for (i = 0; i < KEX_COOKIE_LEN; i++) {          for (i = 0; i < KEX_COOKIE_LEN; i++) {
                 if (i % 4 == 0)                  if (i % 4 == 0)
                         rand = arc4random();                          rand = arc4random();
                 cookie[i] = rand & 0xff;                  buffer_put_char(b, rand & 0xff);
                 rand >>= 8;                  rand >>= 8;
         }          }
         buffer_init(ki);  
         buffer_append(ki, (char *)cookie, sizeof cookie);  
         for (i = 0; i < PROPOSAL_MAX; i++)          for (i = 0; i < PROPOSAL_MAX; i++)
                 buffer_put_cstring(ki, myproposal[i]);                  buffer_put_cstring(b, proposal[i]);
         buffer_put_char(ki, first_kex_packet_follows);          buffer_put_char(b, 0);                  /* first_kex_packet_follows */
         buffer_put_int(ki, 0);                          /* uint32 reserved */          buffer_put_int(b, 0);                   /* uint32 reserved */
         return ki;  
 }  }
   
 /* send kexinit, parse and save reply */  /* parse buffer and return algorithm proposal */
 void  static char **
 kex_exchange_kexinit(  kex_buf2prop(Buffer *raw)
     Buffer *my_kexinit, Buffer *peer_kexint,  
     char *peer_proposal[PROPOSAL_MAX])  
 {  {
           Buffer b;
         int i;          int i;
         char *ptr;          char **proposal;
         int plen;  
   
         debug("send KEXINIT");          proposal = xmalloc(PROPOSAL_MAX * sizeof(char *));
         packet_start(SSH2_MSG_KEXINIT);  
         packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit));  
         packet_send();  
         packet_write_wait();  
         debug("done");  
   
         /*          buffer_init(&b);
          * read and save raw KEXINIT payload in buffer. this is used during          buffer_append(&b, buffer_ptr(raw), buffer_len(raw));
          * computation of the session_id and the session keys.  
          */  
         debug("wait KEXINIT");  
         packet_read_expect(&plen, SSH2_MSG_KEXINIT);  
         ptr = packet_get_raw(&plen);  
         buffer_append(peer_kexint, ptr, plen);  
   
         /* parse packet and save algorithm proposal */  
         /* skip cookie */          /* skip cookie */
         for (i = 0; i < KEX_COOKIE_LEN; i++)          for (i = 0; i < KEX_COOKIE_LEN; i++)
                 packet_get_char();                  buffer_get_char(&b);
         /* extract kex init proposal strings */          /* extract kex init proposal strings */
         for (i = 0; i < PROPOSAL_MAX; i++) {          for (i = 0; i < PROPOSAL_MAX; i++) {
                 peer_proposal[i] = packet_get_string(NULL);                  proposal[i] = buffer_get_string(&b,NULL);
                 debug("got kexinit: %s", peer_proposal[i]);                  debug2("kex_parse_kexinit: %s", proposal[i]);
         }          }
         /* first kex follow / reserved */          /* first kex follows / reserved */
         i = packet_get_char();          i = buffer_get_char(&b);
         debug("first kex follow: %d ", i);          debug2("kex_parse_kexinit: first_kex_follows %d ", i);
         i = packet_get_int();          i = buffer_get_int(&b);
         debug("reserved: %d ", i);          debug2("kex_parse_kexinit: reserved %d ", i);
         packet_done();          buffer_free(&b);
         debug("done");          return proposal;
 }  }
   
 /* diffie-hellman-group1-sha1 */  static void
   kex_prop_free(char **proposal)
 int  
 dh_pub_is_valid(DH *dh, BIGNUM *dh_pub)  
 {  {
         int i;          int i;
         int n = BN_num_bits(dh_pub);  
         int bits_set = 0;  
   
         if (dh_pub->neg) {          for (i = 0; i < PROPOSAL_MAX; i++)
                 log("invalid public DH value: negativ");                  xfree(proposal[i]);
                 return 0;          xfree(proposal);
         }  
         for (i = 0; i <= n; i++)  
                 if (BN_is_bit_set(dh_pub, i))  
                         bits_set++;  
         debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p));  
   
         /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */  
         if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1))  
                 return 1;  
         log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p));  
         return 0;  
 }  }
   
 DH *  static void
 dh_gen_key(DH *dh)  kex_protocol_error(int type, int plen, void *ctxt)
 {  {
         int tries = 0;          error("Hm, kex protocol error: type %d plen %d", type, plen);
   
         do {  
                 if (DH_generate_key(dh) == 0)  
                         fatal("DH_generate_key");  
                 if (tries++ > 10)  
                         fatal("dh_new_group1: too many bad keys: giving up");  
         } while (!dh_pub_is_valid(dh, dh->pub_key));  
         return dh;  
 }  }
   
 DH *  static void
 dh_new_group_asc(const char *gen, const char *modulus)  kex_clear_dispatch(void)
 {  {
         DH *dh;          int i;
         int ret;  
   
         dh = DH_new();          /* Numbers 30-49 are used for kex packets */
         if (dh == NULL)          for (i = 30; i <= 49; i++)
                 fatal("DH_new");                  dispatch_set(i, &kex_protocol_error);
   
         if ((ret = BN_hex2bn(&dh->p, modulus)) < 0)  
                 fatal("BN_hex2bn p");  
         if ((ret = BN_hex2bn(&dh->g, gen)) < 0)  
                 fatal("BN_hex2bn g");  
   
         return (dh_gen_key(dh));  
 }  }
   
 DH *  void
 dh_new_group(BIGNUM *gen, BIGNUM *modulus)  kex_finish(Kex *kex)
 {  {
         DH *dh;          int plen;
   
         dh = DH_new();          kex_clear_dispatch();
         if (dh == NULL)  
                 fatal("DH_new");  
         dh->p = modulus;  
         dh->g = gen;  
   
         return (dh_gen_key(dh));          packet_start(SSH2_MSG_NEWKEYS);
 }          packet_send();
           /* packet_write_wait(); */
           debug("SSH2_MSG_NEWKEYS sent");
   
 DH *          debug("waiting for SSH2_MSG_NEWKEYS");
 dh_new_group1()          packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
 {          debug("SSH2_MSG_NEWKEYS received");
         static char *gen = "2", *group1 =  
             "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"  
             "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"  
             "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"  
             "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"  
             "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"  
             "FFFFFFFF" "FFFFFFFF";  
   
         return (dh_new_group_asc(gen, group1));          kex->done = 1;
           buffer_clear(&kex->peer);
           /* buffer_clear(&kex->my); */
           kex->flags &= ~KEX_INIT_SENT;
           xfree(kex->name);
           kex->name = NULL;
 }  }
   
 void  void
 dump_digest(unsigned char *digest, int len)  kex_send_kexinit(Kex *kex)
 {  {
         int i;          if (kex == NULL) {
         for (i = 0; i< len; i++){                  error("kex_send_kexinit: no kex, cannot rekey");
                 fprintf(stderr, "%02x", digest[i]);                  return;
                 if(i%2!=0)  
                         fprintf(stderr, " ");  
         }          }
         fprintf(stderr, "\n");          if (kex->flags & KEX_INIT_SENT) {
                   debug("KEX_INIT_SENT");
                   return;
           }
           kex->done = 0;
           packet_start(SSH2_MSG_KEXINIT);
           packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
           packet_send();
           debug("SSH2_MSG_KEXINIT sent");
           kex->flags |= KEX_INIT_SENT;
 }  }
   
 unsigned char *  void
 kex_hash(  kex_input_kexinit(int type, int plen, void *ctxt)
     char *client_version_string,  
     char *server_version_string,  
     char *ckexinit, int ckexinitlen,  
     char *skexinit, int skexinitlen,  
     char *serverhostkeyblob, int sbloblen,  
     BIGNUM *client_dh_pub,  
     BIGNUM *server_dh_pub,  
     BIGNUM *shared_secret)  
 {  {
         Buffer b;          char *ptr;
         static unsigned char digest[EVP_MAX_MD_SIZE];          int dlen;
         EVP_MD *evp_md = EVP_sha1();          int i;
         EVP_MD_CTX md;          Kex *kex = (Kex *)ctxt;
   
         buffer_init(&b);          debug("SSH2_MSG_KEXINIT received");
         buffer_put_string(&b, client_version_string, strlen(client_version_string));          if (kex == NULL)
         buffer_put_string(&b, server_version_string, strlen(server_version_string));                  fatal("kex_input_kexinit: no kex, cannot rekey");
   
         /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */          ptr = packet_get_raw(&dlen);
         buffer_put_int(&b, ckexinitlen+1);          buffer_append(&kex->peer, ptr, dlen);
         buffer_put_char(&b, SSH2_MSG_KEXINIT);  
         buffer_append(&b, ckexinit, ckexinitlen);  
         buffer_put_int(&b, skexinitlen+1);  
         buffer_put_char(&b, SSH2_MSG_KEXINIT);  
         buffer_append(&b, skexinit, skexinitlen);  
   
         buffer_put_string(&b, serverhostkeyblob, sbloblen);          /* discard packet */
         buffer_put_bignum2(&b, client_dh_pub);          for (i = 0; i < KEX_COOKIE_LEN; i++)
         buffer_put_bignum2(&b, server_dh_pub);                  packet_get_char();
         buffer_put_bignum2(&b, shared_secret);          for (i = 0; i < PROPOSAL_MAX; i++)
                   xfree(packet_get_string(NULL));
 #ifdef DEBUG_KEX          packet_get_char();
         buffer_dump(&b);          packet_get_int();
 #endif          packet_done();
   
         EVP_DigestInit(&md, evp_md);          kex_kexinit_finish(kex);
         EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));  
         EVP_DigestFinal(&md, digest, NULL);  
   
         buffer_free(&b);  
   
 #ifdef DEBUG_KEX  
         dump_digest(digest, evp_md->md_size);  
 #endif  
         return digest;  
 }  }
   
 unsigned char *  Kex *
 kex_hash_gex(  kex_setup(char *proposal[PROPOSAL_MAX])
     char *client_version_string,  
     char *server_version_string,  
     char *ckexinit, int ckexinitlen,  
     char *skexinit, int skexinitlen,  
     char *serverhostkeyblob, int sbloblen,  
     int minbits, BIGNUM *prime, BIGNUM *gen,  
     BIGNUM *client_dh_pub,  
     BIGNUM *server_dh_pub,  
     BIGNUM *shared_secret)  
 {  {
         Buffer b;          Kex *kex;
         static unsigned char digest[EVP_MAX_MD_SIZE];  
         EVP_MD *evp_md = EVP_sha1();  
         EVP_MD_CTX md;  
   
         buffer_init(&b);          kex = xmalloc(sizeof(*kex));
         buffer_put_string(&b, client_version_string, strlen(client_version_string));          memset(kex, 0, sizeof(*kex));
         buffer_put_string(&b, server_version_string, strlen(server_version_string));          buffer_init(&kex->peer);
           buffer_init(&kex->my);
           kex_prop2buf(&kex->my, proposal);
           kex->done = 0;
   
         /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */          kex_send_kexinit(kex);                                  /* we start */
         buffer_put_int(&b, ckexinitlen+1);          kex_clear_dispatch();
         buffer_put_char(&b, SSH2_MSG_KEXINIT);          dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
         buffer_append(&b, ckexinit, ckexinitlen);  
         buffer_put_int(&b, skexinitlen+1);  
         buffer_put_char(&b, SSH2_MSG_KEXINIT);  
         buffer_append(&b, skexinit, skexinitlen);  
   
         buffer_put_string(&b, serverhostkeyblob, sbloblen);          return kex;
         buffer_put_int(&b, minbits);  
         buffer_put_bignum2(&b, prime);  
         buffer_put_bignum2(&b, gen);  
         buffer_put_bignum2(&b, client_dh_pub);  
         buffer_put_bignum2(&b, server_dh_pub);  
         buffer_put_bignum2(&b, shared_secret);  
   
 #ifdef DEBUG_KEX  
         buffer_dump(&b);  
 #endif  
   
         EVP_DigestInit(&md, evp_md);  
         EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));  
         EVP_DigestFinal(&md, digest, NULL);  
   
         buffer_free(&b);  
   
 #ifdef DEBUG_KEX  
         dump_digest(digest, evp_md->md_size);  
 #endif  
         return digest;  
 }  }
   
 unsigned char *  static void
 derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret)  kex_kexinit_finish(Kex *kex)
 {  {
         Buffer b;          if (!(kex->flags & KEX_INIT_SENT))
         EVP_MD *evp_md = EVP_sha1();                  kex_send_kexinit(kex);
         EVP_MD_CTX md;  
         char c = id;  
         int have;  
         int mdsz = evp_md->md_size;  
         unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);  
   
         buffer_init(&b);          kex_choose_conf(kex);
         buffer_put_bignum2(&b, shared_secret);  
   
         EVP_DigestInit(&md, evp_md);          switch(kex->kex_type) {
         EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));  /* shared_secret K */          case DH_GRP1_SHA1:
         EVP_DigestUpdate(&md, hash, mdsz);              /* transport-06 */                  kexdh(kex);
         EVP_DigestUpdate(&md, &c, 1);                   /* key id */                  break;
         EVP_DigestUpdate(&md, hash, mdsz);              /* session id */          case DH_GEX_SHA1:
         EVP_DigestFinal(&md, digest, NULL);                  kexgex(kex);
                   break;
         /* expand */          default:
         for (have = mdsz; need > have; have += mdsz) {                  fatal("Unsupported key exchange %d", kex->kex_type);
                 EVP_DigestInit(&md, evp_md);  
                 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));  
                 EVP_DigestUpdate(&md, hash, mdsz);  
                 EVP_DigestUpdate(&md, digest, have);  
                 EVP_DigestFinal(&md, digest + have, NULL);  
         }          }
         buffer_free(&b);  
 #ifdef DEBUG_KEX  
         fprintf(stderr, "Digest '%c'== ", c);  
         dump_digest(digest, need);  
 #endif  
         return digest;  
 }  }
   
 #define NKEYS   6  static void
   
 #define MAX_PROP        20  
 #define SEP     ","  
   
 char *  
 get_match(char *client, char *server)  
 {  
         char *sproposals[MAX_PROP];  
         char *c, *s, *p, *ret, *cp, *sp;  
         int i, j, nproposals;  
   
         c = cp = xstrdup(client);  
         s = sp = xstrdup(server);  
   
         for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0';  
              (p = strsep(&sp, SEP)), i++) {  
                 if (i < MAX_PROP)  
                         sproposals[i] = p;  
                 else  
                         break;  
         }  
         nproposals = i;  
   
         for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0';  
              (p = strsep(&cp, SEP)), i++) {  
                 for (j = 0; j < nproposals; j++) {  
                         if (strcmp(p, sproposals[j]) == 0) {  
                                 ret = xstrdup(p);  
                                 xfree(c);  
                                 xfree(s);  
                                 return ret;  
                         }  
                 }  
         }  
         xfree(c);  
         xfree(s);  
         return NULL;  
 }  
 void  
 choose_enc(Enc *enc, char *client, char *server)  choose_enc(Enc *enc, char *client, char *server)
 {  {
         char *name = get_match(client, server);          char *name = match_list(client, server, NULL);
         if (name == NULL)          if (name == NULL)
                 fatal("no matching cipher found: client %s server %s", client, server);                  fatal("no matching cipher found: client %s server %s", client, server);
         enc->cipher = cipher_by_name(name);          enc->cipher = cipher_by_name(name);
Line 402 
Line 245 
         enc->iv = NULL;          enc->iv = NULL;
         enc->key = NULL;          enc->key = NULL;
 }  }
 void  static void
 choose_mac(Mac *mac, char *client, char *server)  choose_mac(Mac *mac, char *client, char *server)
 {  {
         char *name = get_match(client, server);          char *name = match_list(client, server, NULL);
         if (name == NULL)          if (name == NULL)
                 fatal("no matching mac found: client %s server %s", client, server);                  fatal("no matching mac found: client %s server %s", client, server);
         if (strcmp(name, "hmac-md5") == 0) {          if (mac_init(mac, name) < 0)
                 mac->md = EVP_md5();  
         } else if (strcmp(name, "hmac-sha1") == 0) {  
                 mac->md = EVP_sha1();  
         } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) {  
                 mac->md = EVP_ripemd160();  
         } else {  
                 fatal("unsupported mac %s", name);                  fatal("unsupported mac %s", name);
         }          /* truncate the key */
           if (datafellows & SSH_BUG_HMAC)
                   mac->key_len = 16;
         mac->name = name;          mac->name = name;
         mac->mac_len = mac->md->md_size;  
         mac->key_len = (datafellows & SSH_BUG_HMAC) ? 16 : mac->mac_len;  
         mac->key = NULL;          mac->key = NULL;
         mac->enabled = 0;          mac->enabled = 0;
 }  }
 void  static void
 choose_comp(Comp *comp, char *client, char *server)  choose_comp(Comp *comp, char *client, char *server)
 {  {
         char *name = get_match(client, server);          char *name = match_list(client, server, NULL);
         if (name == NULL)          if (name == NULL)
                 fatal("no matching comp found: client %s server %s", client, server);                  fatal("no matching comp found: client %s server %s", client, server);
         if (strcmp(name, "zlib") == 0) {          if (strcmp(name, "zlib") == 0) {
Line 438 
Line 275 
         }          }
         comp->name = name;          comp->name = name;
 }  }
 void  static void
 choose_kex(Kex *k, char *client, char *server)  choose_kex(Kex *k, char *client, char *server)
 {  {
         k->name = get_match(client, server);          k->name = match_list(client, server, NULL);
         if (k->name == NULL)          if (k->name == NULL)
                 fatal("no kex alg");                  fatal("no kex alg");
         if (strcmp(k->name, KEX_DH1) == 0) {          if (strcmp(k->name, KEX_DH1) == 0) {
Line 451 
Line 288 
         } else          } else
                 fatal("bad kex alg %s", k->name);                  fatal("bad kex alg %s", k->name);
 }  }
 void  static void
 choose_hostkeyalg(Kex *k, char *client, char *server)  choose_hostkeyalg(Kex *k, char *client, char *server)
 {  {
         k->hostkeyalg = get_match(client, server);          char *hostkeyalg = match_list(client, server, NULL);
         if (k->hostkeyalg == NULL)          if (hostkeyalg == NULL)
                 fatal("no hostkey alg");                  fatal("no hostkey alg");
         if (strcmp(k->hostkeyalg, KEX_DSS) != 0)          k->hostkey_type = key_type_from_name(hostkeyalg);
                 fatal("bad hostkey alg %s", k->hostkeyalg);          if (k->hostkey_type == KEY_UNSPEC)
                   fatal("bad hostkey alg '%s'", hostkeyalg);
           xfree(hostkeyalg);
 }  }
   
 Kex *  static void
 kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server)  kex_choose_conf(Kex *kex)
 {  {
           Newkeys *newkeys;
           char **my, **peer;
           char **cprop, **sprop;
           int nenc, nmac, ncomp;
         int mode;          int mode;
         int ctos;                               /* direction: if true client-to-server */          int ctos;                               /* direction: if true client-to-server */
         int need;          int need;
         Kex *k;  
   
         k = xmalloc(sizeof(*k));          my   = kex_buf2prop(&kex->my);
         memset(k, 0, sizeof(*k));          peer = kex_buf2prop(&kex->peer);
         k->server = server;  
   
           if (kex->server) {
                   cprop=peer;
                   sprop=my;
           } else {
                   cprop=my;
                   sprop=peer;
           }
   
           /* Algorithm Negotiation */
         for (mode = 0; mode < MODE_MAX; mode++) {          for (mode = 0; mode < MODE_MAX; mode++) {
                 int nenc, nmac, ncomp;                  newkeys = xmalloc(sizeof(*newkeys));
                 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);                  memset(newkeys, 0, sizeof(*newkeys));
                   kex->newkeys[mode] = newkeys;
                   ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
                 nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;                  nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
                 nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;                  nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
                 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;                  ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
                 choose_enc (&k->enc [mode], cprop[nenc],  sprop[nenc]);                  choose_enc (&newkeys->enc,  cprop[nenc],  sprop[nenc]);
                 choose_mac (&k->mac [mode], cprop[nmac],  sprop[nmac]);                  choose_mac (&newkeys->mac,  cprop[nmac],  sprop[nmac]);
                 choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]);                  choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]);
                 debug("kex: %s %s %s %s",                  debug("kex: %s %s %s %s",
                     ctos ? "client->server" : "server->client",                      ctos ? "client->server" : "server->client",
                     k->enc[mode].name,                      newkeys->enc.name,
                     k->mac[mode].name,                      newkeys->mac.name,
                     k->comp[mode].name);                      newkeys->comp.name);
         }          }
         choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);          choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
         choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],          choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
             sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);              sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
         need = 0;          need = 0;
         for (mode = 0; mode < MODE_MAX; mode++) {          for (mode = 0; mode < MODE_MAX; mode++) {
             if (need < k->enc[mode].cipher->key_len)                  newkeys = kex->newkeys[mode];
                     need = k->enc[mode].cipher->key_len;                  if (need < newkeys->enc.cipher->key_len)
             if (need < k->enc[mode].cipher->block_size)                          need = newkeys->enc.cipher->key_len;
                     need = k->enc[mode].cipher->block_size;                  if (need < newkeys->enc.cipher->block_size)
             if (need < k->mac[mode].key_len)                          need = newkeys->enc.cipher->block_size;
                     need = k->mac[mode].key_len;                  if (need < newkeys->mac.key_len)
                           need = newkeys->mac.key_len;
         }          }
         /* XXX need runden? */          /* XXX need runden? */
         k->we_need = need;          kex->we_need = need;
         return k;  
           kex_prop_free(my);
           kex_prop_free(peer);
 }  }
   
 int  static u_char *
 kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret)  derive_key(Kex *kex, int id, int need, u_char *hash, BIGNUM *shared_secret)
 {  {
         int i;          Buffer b;
         int mode;          EVP_MD *evp_md = EVP_sha1();
         int ctos;          EVP_MD_CTX md;
         unsigned char *keys[NKEYS];          char c = id;
           int have;
           int mdsz = evp_md->md_size;
           u_char *digest = xmalloc(roundup(need, mdsz));
   
           buffer_init(&b);
           buffer_put_bignum2(&b, shared_secret);
   
           /* K1 = HASH(K || H || "A" || session_id) */
           EVP_DigestInit(&md, evp_md);
           if (!(datafellows & SSH_BUG_DERIVEKEY))
                   EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
           EVP_DigestUpdate(&md, hash, mdsz);
           EVP_DigestUpdate(&md, &c, 1);
           EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len);
           EVP_DigestFinal(&md, digest, NULL);
   
           /*
            * expand key:
            * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
            * Key = K1 || K2 || ... || Kn
            */
           for (have = mdsz; need > have; have += mdsz) {
                   EVP_DigestInit(&md, evp_md);
                   if (!(datafellows & SSH_BUG_DERIVEKEY))
                           EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
                   EVP_DigestUpdate(&md, hash, mdsz);
                   EVP_DigestUpdate(&md, digest, have);
                   EVP_DigestFinal(&md, digest + have, NULL);
           }
           buffer_free(&b);
   #ifdef DEBUG_KEX
           fprintf(stderr, "key '%c'== ", c);
           dump_digest("key", digest, need);
   #endif
           return digest;
   }
   
   Newkeys *current_keys[MODE_MAX];
   
   #define NKEYS   6
   void
   kex_derive_keys(Kex *kex, u_char *hash, BIGNUM *shared_secret)
   {
           u_char *keys[NKEYS];
           int i, mode, ctos;
   
         for (i = 0; i < NKEYS; i++)          for (i = 0; i < NKEYS; i++)
                 keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret);                  keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, shared_secret);
   
           debug("kex_derive_keys");
         for (mode = 0; mode < MODE_MAX; mode++) {          for (mode = 0; mode < MODE_MAX; mode++) {
                 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);                  current_keys[mode] = kex->newkeys[mode];
                 k->enc[mode].iv  = keys[ctos ? 0 : 1];                  kex->newkeys[mode] = NULL;
                 k->enc[mode].key = keys[ctos ? 2 : 3];                  ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
                 k->mac[mode].key = keys[ctos ? 4 : 5];                  current_keys[mode]->enc.iv  = keys[ctos ? 0 : 1];
                   current_keys[mode]->enc.key = keys[ctos ? 2 : 3];
                   current_keys[mode]->mac.key = keys[ctos ? 4 : 5];
         }          }
         return 0;  
 }  }
   
   Newkeys *
   kex_get_newkeys(int mode)
   {
           Newkeys *ret;
   
           ret = current_keys[mode];
           current_keys[mode] = NULL;
           return ret;
   }
   
   #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
   void
   dump_digest(char *msg, u_char *digest, int len)
   {
           int i;
   
           fprintf(stderr, "%s\n", msg);
           for (i = 0; i< len; i++){
                   fprintf(stderr, "%02x", digest[i]);
                   if (i%32 == 31)
                           fprintf(stderr, "\n");
                   else if (i%8 == 7)
                           fprintf(stderr, " ");
           }
           fprintf(stderr, "\n");
   }
   #endif

Legend:
Removed from v.1.12  
changed lines
  Added in v.1.12.2.5