Skip to content
Snippets Groups Projects
Select Git revision
  • 0c029ca03bd9f5853e727e7ed335a1b4ea37b487
  • master default
  • wip-add-ed25519
  • disable-sha1
  • lsh-2.0.4
  • experimental-20050201
  • lsh-1.4.2
  • lsh-1.2
  • lsh_2.1_release_20130626
  • converted-master-branch-to-git
  • nettle_2.4_release_20110903
  • nettle_2.3_release_20110902
  • nettle_2.2_release_20110711
  • nettle_2.1_release_20100725
  • camellia_32bit_20100720
  • nettle_2.0_release_20090608
  • converted-lsh-2.0.4-branch-to-git
  • lsh_2.0.4_release_20070905
  • lsh_2.9_exp_release_20070404
  • nettle_1.15_release_20061128
  • after_experimental_merge_20060516
  • branch_before_experimental_merge_20060516
  • converted-experimental-branch-to-git
  • head_before_experimental_merge_20060516
  • lsh_2.0.3_release_20060509
  • lsh_2.0.2_release_20060127
  • nettle_1.14_release_20051205
  • nettle_1.13_release_20051006
28 results

algorithms.c

Blame
  • algorithms.c 13.49 KiB
    /* algorithms.c
     *
     * Translate algorithm identifiers (or names) to algorithm objects.
     *
     */
    
    /* lsh, an implementation of the ssh protocol
     *
     * Copyright (C) 1998 Niels Möller
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License as
     * published by the Free Software Foundation; either version 2 of the
     * License, or (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful, but
     * WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     * General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */
    
    #if HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include "algorithms.h"
    
    #include "atoms.h"
    #include "compress.h"
    #include "crypto.h"
    #include "xalloc.h"
    
    #include "lsh_argp.h"
    
    #include <assert.h>
    #include <stdarg.h>
    #include <string.h>
    
    #define GABA_DEFINE
    #include "algorithms.h.x"
    #undef GABA_DEFINE
    
    struct alist *
    all_symmetric_algorithms()
    {
      return make_alist(12
    #if WITH_ZLIB
    		    +1
    #endif
    		    ,
    		    ATOM_ARCFOUR, &crypto_arcfour_algorithm,
    		    ATOM_BLOWFISH_CBC, &crypto_blowfish_cbc_algorithm,
    		    ATOM_TWOFISH_CBC, &crypto_twofish256_cbc_algorithm,
                        ATOM_AES128_CBC, &crypto_aes128_cbc_algorithm,
                        ATOM_AES128_CTR, &crypto_aes128_ctr_algorithm,
                        ATOM_AES256_CBC, &crypto_aes256_cbc_algorithm,
                        ATOM_AES256_CTR, &crypto_aes256_ctr_algorithm,
    		    ATOM_SERPENT256_CBC, &crypto_serpent256_cbc_algorithm,
    		    ATOM_3DES_CBC, &crypto_des3_cbc_algorithm,
    		    ATOM_CAST128_CBC, &crypto_cast128_cbc_algorithm,
    		    ATOM_HMAC_SHA1,
    		      make_hmac_algorithm(&nettle_sha1),
    		    ATOM_HMAC_MD5,
    		      make_hmac_algorithm(&nettle_md5),
    #if WITH_ZLIB
    		    ATOM_ZLIB, make_zlib(),
    #endif
    		    -1);
    }
    
    /* This is used for spki operations, and should therefore use spki names.
     * Note that md5 signatures are not currently supported. */
    struct alist *
    all_signature_algorithms(void)
    {
      return make_alist(3,
    		    ATOM_DSA, &dsa_algorithm,
    		    ATOM_RSA_PKCS1, &rsa_sha1_algorithm,
    		    ATOM_RSA_PKCS1_SHA1, &rsa_sha1_algorithm,
    		    -1);
    }
    
    /* Forward declaration */
    struct int_list *
    filter_algorithms_l(struct alist *algorithms, unsigned n, ...);
    
    /* Includes only reasonably old algorithms and well studied
     * algorithms. */
    struct int_list *
    default_crypto_algorithms(struct alist *algorithms)
    {
      return filter_algorithms_l(algorithms, 4,
    			     ATOM_AES128_CTR,
    			     ATOM_3DES_CBC,
    			     ATOM_BLOWFISH_CBC,
    			     ATOM_ARCFOUR, -1);
    }
    
    /* Includes all supported algorithms, except none. In effect, the
     * peer is trusted in choosing an adequate algorithm. */
    static struct int_list *
    all_crypto_algorithms(struct alist *algorithms)
    {
      return filter_algorithms_l(algorithms, 10,
                                 ATOM_AES128_CBC,
                                 ATOM_AES128_CTR,
                                 ATOM_AES256_CBC,
                                 ATOM_AES256_CTR,
    			     ATOM_3DES_CBC,
    			     ATOM_TWOFISH_CBC, 
    			     ATOM_CAST128_CBC,
    			     ATOM_SERPENT256_CBC,
    			     ATOM_BLOWFISH_CBC,
    			     ATOM_ARCFOUR, -1);
    }
    
    struct int_list *
    default_mac_algorithms(struct alist *algorithms)
    {
      return filter_algorithms_l(algorithms, 2, ATOM_HMAC_SHA1, ATOM_HMAC_MD5, -1);
    }
    
    struct int_list *
    default_compression_algorithms(struct alist *algorithms)
    {
      return filter_algorithms_l(algorithms, 2, ATOM_NONE, ATOM_ZLIB, -1);
    }
    
    /* NOTE: Unfiltered */
    struct int_list *
    default_hostkey_algorithms(void)
    {
      return make_int_list(2, ATOM_SSH_RSA, ATOM_SSH_DSS, -1);
    }
    
    static struct int_list *
    prefer_compression_algorithms(struct alist *algorithms)
    {
      return filter_algorithms_l(algorithms, 2, ATOM_ZLIB, ATOM_NONE, -1);
    }
    
    
    /* This is not really efficient, but it doesn't matter. */
    static int strcasecmp_list(const char *name, ...)
    {
      va_list args;
      char *s;
      int res = 0;
      
      va_start(args, name);
      while ( (s = va_arg(args, char *)) )
        {
          if (!strcasecmp(name, s))
    	{
    	  res = 1;
    	  break;
    	}
        }
      va_end(args);
    
      return res;
    }
        
    int
    lookup_crypto(struct alist *algorithms, const char *name, struct crypto_algorithm **ap)
    {
      int atom;
    
      if (!strcasecmp(name, "none"))
        {
          if (ap)
    	*ap = NULL;
          
          return ATOM_NONE;
        }
      else if (strcasecmp_list(name, "arcfour", NULL))
        atom = ATOM_ARCFOUR;
      else if (strcasecmp_list(name, "twofish-cbc", "twofish", NULL))
        atom = ATOM_TWOFISH_CBC;
      else if (strcasecmp_list(name, "blowfish-cbc", "blowfish", NULL))
        atom = ATOM_BLOWFISH_CBC;
      else if (strcasecmp_list(name, "3des-cbc", "3des", NULL))
        atom = ATOM_3DES_CBC;
      else if (strcasecmp_list(name, "aes128-cbc", "aes-cbc", NULL))
        atom = ATOM_AES128_CBC;
      else if (strcasecmp_list(name, "aes128-ctr", "aes-ctr", "aes", "aes128", NULL))
        atom = ATOM_AES128_CTR;
      else if (strcasecmp_list(name, "aes256-cbc", NULL))
        atom = ATOM_AES256_CBC;
      else if (strcasecmp_list(name, "aes256-ctr", NULL))
        atom = ATOM_AES256_CTR;
      else if (strcasecmp_list(name, "serpent256-cbc",
    			   "serpent-cbc", "serpent", NULL))
        atom = ATOM_SERPENT256_CBC;
      else if (strcasecmp_list(name, "cast128-cbc", "cast",
    			   "cast-cbc", "cast128", NULL))
        atom = ATOM_CAST128_CBC;
      else
        return 0;
    
      /* Is this crypto supported? */
      {
        CAST_SUBTYPE(crypto_algorithm, a, ALIST_GET(algorithms, atom));
        if (a)
          {
    	if (ap)
    	  *ap = a;
    
    	return atom;
          }
        else
          return 0;
      }
    }
    
    int
    lookup_mac(struct alist *algorithms, const char *name, struct mac_algorithm **ap)
    {
      int atom;
    
      if (!strcasecmp(name, "none"))
        {
          if (ap)
    	*ap = NULL;
    
          return ATOM_NONE;
        }
      if (strcasecmp_list(name, "hmac-sha1", "sha", "hmac-sha", "sha1", NULL))
        atom = ATOM_HMAC_SHA1;
      else if (strcasecmp_list(name, "hmac-md5", "md5", NULL))
        atom = ATOM_HMAC_MD5;
      else
        return 0;
      
      /* Is this mac supported? */
      {
        CAST_SUBTYPE(mac_algorithm, a, ALIST_GET(algorithms, atom));
        if (a)
          {
    	if (ap)
    	  *ap = a;
    
    	return atom;
          }
        else
          return 0;
      }
    }
    
    int
    lookup_compression(struct alist *algorithms, const char *name, struct compress_algorithm **ap)
    {
      int atom;
    
      if (!strcasecmp(name, "none"))
        {
          if (ap)
    	*ap = NULL;
    
          return ATOM_NONE;
        }
      if (strcasecmp_list(name, "zlib", "z", NULL))
        atom = ATOM_ZLIB;
      else
        return 0;
      
      /* Is this compression algorithm supported? */
      {
        CAST_SUBTYPE(compress_algorithm, a, ALIST_GET(algorithms, atom));
        if (a)
          {
    	if (ap)
    	  *ap = a;
    
    	return atom;
          }
        else
          return 0;
      }
    }
    
    int
    lookup_hostkey_algorithm(const char *name)
    {
      if (!strcasecmp(name, "none"))
        return ATOM_NONE;
      else if (strcasecmp_list(name, "ssh-dss", "dsa", "dss", NULL))
        return ATOM_SSH_DSS;
      else if (strcasecmp_list(name, "ssh-dsa-sha256", "dsa-sha256",
    			   "dsa2", NULL))
        return ATOM_SSH_DSA_SHA256_LOCAL;
      else if (strcasecmp_list(name, "spki-sign-rsa", "spki-rsa", NULL))
        return ATOM_SPKI_SIGN_RSA;
      else if (strcasecmp_list(name, "spki-sign-dss", "spki-dss",
    			   "spki-sign-dsa", "spki-dsa", NULL))
        return ATOM_SPKI_SIGN_DSS;
      else
        return 0;
    }
    
    /* Return an int list containing the elements of CANDIDATES
     * that have associated values in ALGORITHMS.
     * Returns a non-empty list or NULL. */
     
    struct int_list *
    filter_algorithms(struct alist *algorithms,
    		  const struct int_list *candidates)
    {
      struct int_list *l;
      unsigned i, j;
      unsigned supported;
      
      for (i = 0, supported = 0; i < LIST_LENGTH(candidates); i++)
        {
          int atom = LIST(candidates)[i];
          if (ALIST_GET(algorithms, atom)
    	  || (atom == ATOM_NONE))
    	supported++;
        }
    
      if (!supported)
        return NULL;
      
      l = alloc_int_list(supported);
    
      for (i = j = 0; i < LIST_LENGTH(candidates); i++)
        {
          int atom = LIST(candidates)[i];
          if (ALIST_GET(algorithms, atom)
    	  || (atom == ATOM_NONE))
    	{
    	  assert(j < supported);
    	  LIST(l)[j++] = atom;
    	}
        }
      assert(j == supported);
    
      return l;
    }
    
    
    struct int_list *
    filter_algorithms_l(struct alist *algorithms, unsigned n, ...)
    {
      va_list args;
      struct int_list *l;
      struct int_list *candidates;
      
      va_start(args, n);
      candidates = make_int_listv(n, args);
      va_end(args);
      
      l = filter_algorithms(algorithms, candidates);
    
      assert(LIST_LENGTH(l));
      
      KILL(candidates);
      return l;
    }
    
    
    static void
    list_algorithms(const struct argp_state *state,
    		char *prefix,
    		struct int_list *algorithms,
    		int none)
    {
      unsigned i;
      int separate;
      
      fprintf(state->out_stream, "%s", prefix);
    
      for (i = 0, separate = 0; i<LIST_LENGTH(algorithms); i++)
        {
          if (separate)
    	fprintf(state->out_stream, ", ");
          
          fprintf(state->out_stream, "%s",
    	      /* NOTE: This is the only place where we use that
    	       * atom names are NUL-terminated. */
    	      get_atom_name(LIST(algorithms)[i]));
          separate = 1;
        }
    
      if (none)
        {
          if (separate)
    	fprintf(state->out_stream, ", ");
          fprintf(state->out_stream, "none");
        }
    
      fprintf(state->out_stream, "\n");
    }
    
    void
    list_crypto_algorithms(const struct argp_state *state,
    		       struct alist *algorithms)
    {
      list_algorithms(state,
    		  "Supported crypto algorithms: ",
    		  all_crypto_algorithms(algorithms),
    		  1);
    }
    
    void
    list_mac_algorithms(const struct argp_state *state,
    		    struct alist *algorithms)
    {
      list_algorithms(state,
    		  "Supported MAC algorithms: ",
    		  default_mac_algorithms(algorithms),
    		  1);
    }
    
    void
    list_compression_algorithms(const struct argp_state *state,
    			    struct alist *algorithms)
    {
      list_algorithms(state, 
    		  "Supported compression algorithms: ",
    		  default_compression_algorithms(algorithms),
    		  0);
    }
    
    void
    list_hostkey_algorithms(const struct argp_state *state)
    {
      fprintf(state->out_stream, "%s", "Supported hostkey algorithms: ssh-dss, spki, none\n");
    }
    
    
    #define OPT_LIST_ALGORITHMS 0x100
    #define OPT_HOSTKEY_ALGORITHMS 0x101
    
    static const struct argp_option
    algorithms_options[] =
    {
      /* Name, key, arg-name, flags, doc, group */
      { NULL, 0, NULL, 0, "Algorithm selection:", 0},
      { "crypto", 'c', "ALGORITHM", 0, "", 0 },
      { "compression", 'z', "ALGORITHM",
        OPTION_ARG_OPTIONAL, "Default is zlib.", 0 },
      { "mac", 'm', "ALGORITHM", 0, "", 0 },
      { "hostkey-algorithm", OPT_HOSTKEY_ALGORITHMS, "ALGORITHM", 0, "", 0 }, 
      { "list-algorithms", OPT_LIST_ALGORITHMS, NULL, 0,
        "List supported algorithms.", 0 },
      { NULL, 0, NULL, 0, NULL, 0 }  
    };
    
    void
    init_algorithms_options(struct algorithms_options *self,
    			struct alist *algorithms)
    {
      self->algorithms = algorithms;
    
      self->crypto_algorithms = NULL;
      self->mac_algorithms = NULL;
      self->compression_algorithms = NULL;
      self->hostkey_algorithms = NULL;
    }
    
    struct algorithms_options *
    make_algorithms_options(struct alist *algorithms)
    {
      NEW(algorithms_options, self);
      init_algorithms_options(self, algorithms);
    
      return self;
    }
    
    static error_t
    algorithms_argp_parser(int key, char *arg, struct argp_state *state)
    {
      CAST_SUBTYPE(algorithms_options, self, state->input);
      
      switch(key)
        {
        default:
          return ARGP_ERR_UNKNOWN;
        case ARGP_KEY_END:
          if (!self->crypto_algorithms)
    	self->crypto_algorithms = default_crypto_algorithms(self->algorithms);
          if (!self->mac_algorithms)
    	self->mac_algorithms = default_mac_algorithms(self->algorithms);
          if (!self->compression_algorithms)
    	self->compression_algorithms = default_compression_algorithms(self->algorithms);
          if (!self->hostkey_algorithms)
    	self->hostkey_algorithms = default_hostkey_algorithms();
          break;
        case 'c':
          {
    	int crypto = lookup_crypto(self->algorithms, arg, NULL);
    
    	if (crypto)
    	  self->crypto_algorithms = make_int_list(1, crypto, -1);
    	else if (strcasecmp_list(arg, "all", "any", NULL))
    	  self->crypto_algorithms = all_crypto_algorithms(self->algorithms);
    	
    	else
    	  {
    	    list_crypto_algorithms(state, self->algorithms);
    	    argp_error(state, "Unknown crypto algorithm '%s'.", arg);
    	  }
    	break;
          }
        case 'm':
          {
    	int mac = lookup_mac(self->algorithms, arg, NULL);
    	if (mac)
    	  self->mac_algorithms = make_int_list(1, mac, -1);
    	else
    	  {
    	    list_mac_algorithms(state, self->algorithms);
    	    argp_error(state, "Unknown message authentication algorithm '%s'.", arg);
    	  }	
    	break;
          }
        case 'z':
          {
    	if (!arg)
    	  self->compression_algorithms = prefer_compression_algorithms(self->algorithms);
    	else
    	  {
    	    int compression = lookup_compression(self->algorithms, arg, NULL);
    	    if (compression)
    	      self->compression_algorithms = make_int_list(1, compression, -1);
    	    else
    	      {
    		list_compression_algorithms(state, self->algorithms);
    		argp_error(state, "Unknown compression algorithm '%s'.", arg);
    	      }
    	  }
    	break;
          }
        case OPT_HOSTKEY_ALGORITHMS:
          {
    	int algorithm = lookup_hostkey_algorithm(arg);
    	if (algorithm)
    	  self->hostkey_algorithms = make_int_list(1, algorithm, -1);
    	else
    	  {
    	    list_hostkey_algorithms(state);
    	    argp_error(state, "Unknown hostkey algorithm '%s'.", arg);
    	  }	
    	break;
    	
          }
          
        case OPT_LIST_ALGORITHMS:
          list_crypto_algorithms(state, self->algorithms);
          list_compression_algorithms(state, self->algorithms);
          list_mac_algorithms(state, self->algorithms);
          list_hostkey_algorithms(state);
          
          if (! (state->flags & ARGP_NO_EXIT))
    	exit (0);
        }
      return 0;
    }
    
    const struct argp algorithms_argp =
    {
      algorithms_options,
      algorithms_argp_parser,
      NULL, NULL, NULL, NULL, NULL
    };