Skip to content
Snippets Groups Projects
Select Git revision
  • 3958994ea9dd691bedd449dcde4e45cf96dfe389
  • master default protected
  • 9.0
  • marcus/wix3
  • 8.0
  • nt-tools
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • v8.0.2020
  • v8.0.2018
  • v8.0.2016
  • v8.0.2014
  • v8.0.2012
  • v8.0.2008
  • v8.0.2006
  • v8.0.2004
  • v8.0.2002
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
41 results

docode.c

Blame
  • dsa.c 14.95 KiB
    /* dsa.c
     *
     */
    
    /* lsh, an implementation of the ssh protocol
     *
     * Copyright (C) 1998, 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301  USA
     */
    
    #if HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <assert.h>
    
    #include "nettle/bignum.h"
    #include "nettle/dsa.h"
    #include "nettle/sexp.h"
    #include "nettle/sha.h"
    
    #include "crypto.h"
    
    #include "atoms.h"
    #include "format.h"
    #include "lsh_string.h"
    #include "parse.h"
    #include "randomness.h"
    #include "sexp.h"
    #include "ssh.h"
    #include "werror.h"
    #include "xalloc.h"
    
    #include "dsa.c.x" 
    
    /* The standard says that DSA public keys are at most 1024 bits, i.e.
     * 128 octets. We are a little more liberal than that. Note that
     * allowing really large keys opens for Denial-of-service attacks. */
    
    #define DSA_SHA1_MAX_OCTETS 256
    #define DSA_SHA1_MAX_BITS (8 * DSA_SHA1_MAX_OCTETS)
    
    /* Set limit at 4096 bits, FIPS 186-3 sets limit at 3072 */
    #define DSA_SHA256_MAX_OCTETS 512
    #define DSA_SHA256_MAX_BITS (8 * DSA_SHA256_MAX_OCTETS)
    
    /* DSA signatures */
    
    /* GABA:
       (class
         (name dsa_verifier)
         (super verifier)
         (vars
           (params indirect-special "struct dsa_params"
                #f dsa_params_clear)
           (key bignum)))
    */
    
    /* GABA:
       (class
         (name dsa_signer)
         (super signer)
         (vars
           (verifier object dsa_verifier)
           (key bignum)))
    */
    
    static int
    do_dsa_verify(struct verifier *c, int algorithm,
    	      uint32_t length,
    	      const uint8_t *msg,
    	      uint32_t signature_length,
    	      const uint8_t *signature_data)
    {
      CAST(dsa_verifier, self, c);
      struct sha1_ctx hash;
      uint8_t digest[SHA1_DIGEST_SIZE];
    
      struct simple_buffer buffer;
    
      int res = 0;
    
      struct dsa_signature sv;
    
      trace("do_dsa_verify: Verifying %a signature\n", algorithm);
      dsa_signature_init(&sv);
      
      switch (algorithm)
        {
        case ATOM_SSH_DSS:
          {
    	/* NOTE: draft-ietf-secsh-transport-X.txt (x <= 07) uses an extra
    	 * length field, which should be removed in the next version. */
    	
    	uint32_t buf_length;
    	const uint8_t *buf;
    	enum lsh_atom atom;
          
    	simple_buffer_init(&buffer, signature_length, signature_data);
    	if (!(parse_atom(&buffer, &atom)
    	      && (atom == ATOM_SSH_DSS)
    	      && parse_string(&buffer, &buf_length, &buf)
    	      && buf_length == 2 * DSA_SHA1_Q_OCTETS
    	      && parse_eod(&buffer)))
    	  goto fail;
    
    	nettle_mpz_set_str_256_u(sv.r, DSA_SHA1_Q_OCTETS, buf);
    	nettle_mpz_set_str_256_u(sv.s, DSA_SHA1_Q_OCTETS,
    				 buf + DSA_SHA1_Q_OCTETS);
    
    	break;
          }
    
          /* It doesn't matter here which flavour of SPKI is used. */
        case ATOM_SPKI_SIGN_RSA:
        case ATOM_SPKI_SIGN_DSS:
        case ATOM_SPKI:
          {
    	struct sexp_iterator i;
    	
    	const uint8_t *names[2] = { "r", "s" };
    	struct sexp_iterator values[2];
    	
    	if (! (sexp_iterator_first(&i, signature_length,  signature_data)
    	       && sexp_iterator_enter_list(&i)
    	       && sexp_iterator_assoc(&i, 2, names, values)
    	       && nettle_mpz_set_sexp(sv.r, DSA_SHA1_Q_BITS, &values[0])
    	       && nettle_mpz_set_sexp(sv.s, DSA_SHA1_Q_BITS, &values[1])) )
    	  goto fail;
    
    	break;
          }
        default:
          fatal("do_dsa_verify: Internal error!\n");
        }
    
      sha1_init(&hash);
      sha1_update(&hash, length, msg);
      sha1_digest(&hash, sizeof(digest), digest);
      res = dsa_verify(&self->params, self->key, sizeof(digest), digest, &sv);
     fail:
    
      dsa_signature_clear(&sv);
    
      return res;
    }
    
    
    static struct lsh_string *
    do_dsa_public_key(struct verifier *s)
    {
      CAST(dsa_verifier, self, s);
      return ssh_format("%a%n%n%n%n",
    		    ATOM_SSH_DSS,
    		    self->params.p, self->params.q,
    		    self->params.g, self->key);
    }
    
    /* FIXME: Should maybe switch to the name "dsa-sha1". Not sure what we
       need to do for backwards compatibility. Alternatively, make libspki
       recognize "dsa" as an alias. */
    static struct lsh_string *
    do_dsa_public_spki_key(struct verifier *s, int transport)
    {
      CAST(dsa_verifier, self, s);
    
      return lsh_string_format_sexp(transport,
    				"(%0s(%0s(%0s%b)(%0s%b)(%0s%b)(%0s%b)))",
    				"public-key",  "dsa",
    				"p", self->params.p,
    				"q", self->params.q,
    				"g", self->params.g,
    				"y", self->key);
    }
    
    static void
    init_dsa_verifier(struct dsa_verifier *self)
    {
      dsa_params_init(&self->params);
    
      self->super.verify = do_dsa_verify;
      self->super.public_spki_key = do_dsa_public_spki_key;
      self->super.public_key = do_dsa_public_key;
    }
    
    
    /* Alternative constructor using a key of type ssh-dss, when the atom
     * "ssh-dss" is already read from the buffer. */
    struct verifier *
    parse_ssh_dss_public(struct simple_buffer *buffer)
    {
      NEW(dsa_verifier, res);
      init_dsa_verifier(res);
    
      if (parse_bignum(buffer, res->params.p, DSA_SHA1_MAX_OCTETS)
          && (mpz_sgn(res->params.p) == 1)
          && parse_bignum(buffer, res->params.q, DSA_SHA1_Q_OCTETS)
          && (mpz_sgn(res->params.q) == 1)
          && mpz_sizeinbase(res->params.q, 2) == DSA_SHA1_Q_BITS
          && (mpz_cmp(res->params.q, res->params.p) < 0) /* q < p */ 
          && parse_bignum(buffer, res->params.g, DSA_SHA1_MAX_OCTETS)
          && (mpz_sgn(res->params.g) == 1)
          && (mpz_cmp(res->params.g, res->params.p) < 0) /* g < p */ 
          && parse_bignum(buffer, res->key, DSA_SHA1_MAX_OCTETS) 
          && (mpz_sgn(res->key) == 1)
          && (mpz_cmp(res->key, res->params.p) < 0) /* y < p */
          && parse_eod(buffer))
        
        return &res->super;
    
      else
        {
          KILL(res);
          return NULL;
        }
    }
    
      
    /* Creating signatures */
    
    static void
    dsa_blob_write(struct lsh_string *buf, uint32_t pos,
    	       const struct dsa_signature *signature)
    {
      lsh_string_write_bignum(buf, pos, DSA_SHA1_Q_OCTETS,
    			  signature->r);
      lsh_string_write_bignum(buf, pos + DSA_SHA1_Q_OCTETS, DSA_SHA1_Q_OCTETS,
    			  signature->s);
    }
    
    static struct lsh_string *
    do_dsa_sign(struct signer *c,
    	    int algorithm,
    	    uint32_t msg_length,
    	    const uint8_t *msg)
    {
      CAST(dsa_signer, self, c);
      struct dsa_signature sv;
      struct sha1_ctx hash;
      uint8_t digest[SHA1_DIGEST_SIZE];
      struct lsh_string *signature;
    
      trace("do_dsa_sign: Signing according to %a\n", algorithm);
    
      dsa_signature_init(&sv);
      sha1_init(&hash);
      sha1_update(&hash, msg_length, msg);
      sha1_digest(&hash, sizeof(digest), digest);
    
      if (dsa_sign(&self->verifier->params, self->key,
    	       NULL, lsh_random, sizeof(digest), digest, &sv))
        /* Build signature */
        switch (algorithm)
          {
          case ATOM_SSH_DSS:
    	{
    	  uint32_t blob_pos;
    	
    	  /* NOTE: draft-ietf-secsh-transport-X.txt (x <= 07) uses an extra
    	   * length field, which should be removed in the next version. */
    	  signature = ssh_format("%a%r", ATOM_SSH_DSS,
    				 2 * DSA_SHA1_Q_OCTETS, &blob_pos);
    	  dsa_blob_write(signature, blob_pos, &sv);
    
    	  break;
    	}
    	/* It doesn't matter here which flavour of SPKI is used. */
          case ATOM_SPKI_SIGN_RSA:
          case ATOM_SPKI_SIGN_DSS:
          case ATOM_SPKI:
    	/* Format: "((1:r20:<r>)(1:s20:<s>))". */
    	signature = lsh_string_format_sexp(0, "((r%b)(s%b))",
    					   sv.r, sv.s);
    	
    	break;
          default:
    	fatal("do_dsa_sign: Internal error, unexpected algorithm %a.\n",
    	      algorithm);
          }
      else
        signature = NULL;
    
      dsa_signature_clear(&sv);
      
      return signature;
    }
    
    static struct verifier *
    do_dsa_get_verifier(struct signer *s)
    {
      CAST(dsa_signer, self, s);
      return &self->verifier->super;
    }
    
    
    static struct verifier *
    make_dsa_verifier(struct signature_algorithm *self UNUSED,
    		  struct sexp_iterator *i)
    {
      NEW(dsa_verifier, res);
      init_dsa_verifier(res);
    
      if (dsa_keypair_from_sexp_alist(&res->params, res->key, NULL,
    				  DSA_SHA1_MAX_BITS, DSA_SHA1_Q_BITS,
    				  i))
        return &res->super;
    
      KILL(res);
      return NULL;
    }
    
    static struct signer *
    make_dsa_signer(struct signature_algorithm *self UNUSED,
    		struct sexp_iterator *i)
    {
      NEW(dsa_verifier, verifier);
      NEW(dsa_signer, res);
    
      init_dsa_verifier(verifier);
      
      if (dsa_keypair_from_sexp_alist(&verifier->params, verifier->key, res->key,
    				  DSA_SHA1_MAX_BITS, DSA_SHA1_Q_BITS,
    				  i))
        {
          res->verifier = verifier;
          res->super.sign = do_dsa_sign;
          res->super.get_verifier = do_dsa_get_verifier;
          
          return &res->super;
        }
    
      KILL(res);
      KILL(verifier);
      return NULL;
    }
    
    struct signature_algorithm dsa_algorithm =
      { STATIC_HEADER, make_dsa_signer, make_dsa_verifier };
    
    struct verifier *
    make_ssh_dss_verifier(uint32_t length, const uint8_t *key)
    {
      struct simple_buffer buffer;
      enum lsh_atom atom;
      
      simple_buffer_init(&buffer, length, key);
    
      return ( (parse_atom(&buffer, &atom)
    	    && (atom == ATOM_SSH_DSS))
    	   ? parse_ssh_dss_public(&buffer)
    	   : NULL);
    }
    
    /* And the dsa-sha256 signature algorithm, with the larger sha256 hash
       function */
    
    static int
    do_dsa_sha256_verify(struct verifier *c, int algorithm,
    		     uint32_t length,
    		     const uint8_t *msg,
    		     uint32_t signature_length,
    		     const uint8_t *signature_data)
    {
      CAST(dsa_verifier, self, c);
      struct sha256_ctx hash;
      uint8_t digest[SHA256_DIGEST_SIZE];
    
      struct simple_buffer buffer;
    
      int res = 0;
    
      struct dsa_signature sv;
    
      trace("do_dsa_verify: Verifying %a signature\n", algorithm);
      dsa_signature_init(&sv);
    
      switch (algorithm)
        {
        case ATOM_SSH_DSA_SHA256_LOCAL:
          {
    	enum lsh_atom atom;
          
    	simple_buffer_init(&buffer, signature_length, signature_data);
    	if (!(parse_atom(&buffer, &atom)
    	      && (atom == ATOM_SSH_DSA)
    	      && parse_bignum(&buffer, sv.r, DSA_SHA256_Q_OCTETS)
    	      && mpz_sgn(sv.r) > 0
    	      && parse_bignum(&buffer, sv.s, DSA_SHA256_Q_OCTETS)
    	      && mpz_sgn(sv.s) > 0
    	      && parse_eod(&buffer)))
    	  goto fail;
    
    	break;
          }
    
          /* No spki support for now. */
        default:
          fatal("do_dsa_sha256_verify: Internal error!\n");
        }
    
      sha256_init(&hash);
      sha256_update(&hash, length, msg);
      sha256_digest(&hash, sizeof(digest), digest);
      
      res = dsa_verify(&self->params, self->key, sizeof(digest), digest, &sv);
     fail:
    
      dsa_signature_clear(&sv);
    
      return res;
    }
    
    
    static struct lsh_string *
    do_dsa_sha256_public_key(struct verifier *s)
    {
      CAST(dsa_verifier, self, s);
      return ssh_format("%a%n%n%n%n",
    		    ATOM_SSH_DSA,
    		    self->params.p, self->params.q,
    		    self->params.g, self->key);
    }
    
    static struct lsh_string *
    do_dsa_sha256_public_spki_key(struct verifier *s, int transport)
    {
      CAST(dsa_verifier, self, s);
    
      return lsh_string_format_sexp(transport,
    				"(%0s(%0s(%0s%b)(%0s%b)(%0s%b)(%0s%b)))",
    				"public-key",  "dsa-sha256",
    				"p", self->params.p,
    				"q", self->params.q,
    				"g", self->params.g,
    				"y", self->key);
    }
    
    static void
    init_dsa_sha256_verifier(struct dsa_verifier *self)
    {
      dsa_params_init(&self->params);
    
      self->super.verify = do_dsa_sha256_verify;
      self->super.public_spki_key = do_dsa_sha256_public_spki_key;
      self->super.public_key = do_dsa_sha256_public_key;
    }
    
    /* FIXME: Duplicated code with plain ssh_dss. */
    /* Alternative constructor using a key of type ssh-dsa-sha256, when
     * the atom "ssh-dss" is already read from the buffer. */
    struct verifier *
    parse_ssh_dsa_sha256_public(struct simple_buffer *buffer)
    {
      NEW(dsa_verifier, res);
      init_dsa_verifier(res);
    
      if (parse_bignum(buffer, res->params.p, DSA_SHA256_MAX_OCTETS)
          && (mpz_sgn(res->params.p) == 1)
          && parse_bignum(buffer, res->params.q, DSA_SHA256_Q_OCTETS)
          && (mpz_sgn(res->params.q) == 1)
          && mpz_sizeinbase(res->params.q, 2) == DSA_SHA256_Q_BITS
          && (mpz_cmp(res->params.q, res->params.p) < 0) /* q < p */ 
          && parse_bignum(buffer, res->params.g, DSA_SHA256_MAX_OCTETS)
          && (mpz_sgn(res->params.g) == 1)
          && (mpz_cmp(res->params.g, res->params.p) < 0) /* g < p */ 
          && parse_bignum(buffer, res->key, DSA_SHA256_MAX_OCTETS) 
          && (mpz_sgn(res->key) == 1)
          && (mpz_cmp(res->key, res->params.p) < 0) /* y < p */
          && parse_eod(buffer))
        
        return &res->super;
    
      else
        {
          KILL(res);
          return NULL;
        }
    }
    
      
    /* Creating signatures */
    
    static struct lsh_string *
    do_dsa_sha256_sign(struct signer *c,
    		   int algorithm,
    		   uint32_t msg_length,
    		   const uint8_t *msg)
    {
      CAST(dsa_signer, self, c);
      struct dsa_signature sv;
      struct sha256_ctx hash;
      uint8_t digest[SHA256_DIGEST_SIZE];
      struct lsh_string *signature;
    
      trace("do_dsa_sign: Signing according to %a\n", algorithm);
    
      dsa_signature_init(&sv);
      sha256_init(&hash);
      sha256_update(&hash, msg_length, msg);
      sha256_digest(&hash, sizeof(digest), digest);
    
      if (dsa_sign(&self->verifier->params, self->key,
    	       NULL, lsh_random, sizeof(digest), digest, &sv))
        /* Build signature */
        switch (algorithm)
          {
          case ATOM_SSH_DSA_SHA256_LOCAL:
    	{
    	  signature = ssh_format("%a%n%n", ATOM_SSH_DSA,
    				 sv.r, sv.s);
    
    	  break;
    	}
    	/* It doesn't matter here which flavour of SPKI is used. */
          case ATOM_SPKI_SIGN_RSA:
          case ATOM_SPKI_SIGN_DSS:
          case ATOM_SPKI:
    	/* Format: "((1:r20:<r>)(1:s20:<s>))". */
    	signature = lsh_string_format_sexp(0, "((r%b)(s%b))",
    					   sv.r, sv.s);
    	
    	break;
          default:
    	fatal("do_dsa_sign: Internal error, unexpected algorithm %a.\n",
    	      algorithm);
          }
      else
        signature = NULL;
    
      dsa_signature_clear(&sv);
      
      return signature;
    }
    
    static struct verifier *
    do_dsa_sha256_get_verifier(struct signer *s)
    {
      CAST(dsa_signer, self, s);
      return &self->verifier->super;
    }
    
    static struct verifier *
    make_dsa_sha256_verifier(struct signature_algorithm *self UNUSED,
    			 struct sexp_iterator *i)
    {
      NEW(dsa_verifier, res);
      init_dsa_sha256_verifier(res);
    
      if (dsa_keypair_from_sexp_alist(&res->params, res->key, NULL,
    				  DSA_SHA256_MAX_BITS, DSA_SHA256_Q_BITS,
    				  i))
        return &res->super;
    
      KILL(res);
      return NULL;
    }
    
    static struct signer *
    make_dsa_sha256_signer(struct signature_algorithm *self UNUSED,
    		       struct sexp_iterator *i)
    {
      NEW(dsa_verifier, verifier);
      NEW(dsa_signer, res);
    
      init_dsa_verifier(verifier);
      
      if (dsa_keypair_from_sexp_alist(&verifier->params, verifier->key, res->key,
    				  DSA_SHA256_MAX_BITS, DSA_SHA256_Q_BITS,
    				  i))
        {
          res->verifier = verifier;
          res->super.sign = do_dsa_sha256_sign;
          res->super.get_verifier = do_dsa_sha256_get_verifier;
          
          return &res->super;
        }
    
      KILL(res);
      KILL(verifier);
      return NULL;
    }
    
    struct signature_algorithm dsa_sha256_algorithm =
      { STATIC_HEADER, make_dsa_sha256_signer, make_dsa_sha256_verifier };
    
    struct verifier *
    make_ssh_dsa_sha256_verifier(uint32_t length, const uint8_t *key)
    {
      struct simple_buffer buffer;
      enum lsh_atom atom;
      
      simple_buffer_init(&buffer, length, key);
    
      return ( (parse_atom(&buffer, &atom)
    	    && (atom == ATOM_SSH_DSA_SHA256_LOCAL))
    	   ? parse_ssh_dsa_sha256_public(&buffer)
    	   : NULL);
    }