Exemplo n.º 1
0
/*
 * Write the mpi A to OUT.
 */
gpg_error_t
gpg_mpi_write (iobuf_t out, gcry_mpi_t a)
{
  int rc;

  if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
    {
      unsigned int nbits;
      const void *p;

      p = gcry_mpi_get_opaque (a, &nbits);
      rc = iobuf_write (out, p, (nbits+7)/8);
    }
  else
    {
      char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */
      size_t nbytes;

      nbytes = DIM(buffer);
      rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a );
      if( !rc )
        rc = iobuf_write( out, buffer, nbytes );
      else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT )
        {
          log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a));
          /* The buffer was too small. We better tell the user about the MPI. */
          rc = gpg_error (GPG_ERR_TOO_LARGE);
        }
    }

  return rc;
}
Exemplo n.º 2
0
/**
 * Output the given MPI value to the given buffer in
 * network byte order.
 * The MPI @a val may not be negative.
 *
 * @param buf where to output to
 * @param size number of bytes in @a buf
 * @param val value to write to @a buf
 */
void
GNUNET_CRYPTO_mpi_print_unsigned (void *buf,
                                  size_t size,
                                  gcry_mpi_t val)
{
  size_t rsize;

  if (gcry_mpi_get_flag (val, GCRYMPI_FLAG_OPAQUE))
  {
    /* Store opaque MPIs left aligned into the buffer.  */
    unsigned int nbits;
    const void *p;

    p = gcry_mpi_get_opaque (val, &nbits);
    GNUNET_assert (p);
    rsize = (nbits+7)/8;
    if (rsize > size)
      rsize = size;
    memcpy (buf, p, rsize);
    if (rsize < size)
      memset (buf+rsize, 0, size - rsize);
  }
  else
  {
    /* Store regular MPIs as unsigned integers right aligned into
       the buffer.  */
    rsize = size;
    GNUNET_assert (0 ==
                   gcry_mpi_print (GCRYMPI_FMT_USG, buf, rsize, &rsize,
                                   val));
    adjust (buf, rsize, size);
  }
}
Exemplo n.º 3
0
static void
test_opaque (void)
{
  gcry_mpi_t a;
  char *p;
  unsigned int nbits;

  p = gcry_xstrdup ("This is a test buffer");
  a = gcry_mpi_set_opaque (NULL, p, 21*8+1); /* (a non byte aligned length) */

  if (!gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
    die ("opaque flag not set\n");

  p = gcry_mpi_get_opaque (a, &nbits);
  if (!p)
    die ("gcry_mpi_get_opaque returned NULL\n");
  if (nbits != 21*8+1)
    die ("gcry_mpi_get_opaque returned a changed bit size\n");
  if (strcmp (p, "This is a test buffer"))
    die ("gcry_mpi_get_opaque returned a changed buffer\n");

  if (debug)
    gcry_log_debugmpi ("mpi", a);
  gcry_mpi_release (a);

  p = gcry_xstrdup ("This is a test buffer");
  a = gcry_mpi_set_opaque_copy (NULL, p, 21*8+1);
  gcry_free (p);

  if (!gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
    die ("opaque flag not set\n");

  p = gcry_mpi_get_opaque (a, &nbits);
  if (!p)
    die ("gcry_mpi_get_opaque returned NULL\n");
  if (nbits != 21*8+1)
    die ("gcry_mpi_get_opaque returned a changed bit size\n");
  if (strcmp (p, "This is a test buffer"))
    die ("gcry_mpi_get_opaque returned a changed buffer\n");

  if (debug)
    gcry_log_debugmpi ("mpi", a);

  gcry_mpi_release (a);
}
Exemplo n.º 4
0
static gpg_error_t
write_fake_data (IOBUF out, gcry_mpi_t a)
{
  unsigned int n;
  void *p;

  if (!a)
    return 0;
  p = gcry_mpi_get_opaque ( a, &n);
  return iobuf_write (out, p, (n+7)/8 );
}
Exemplo n.º 5
0
static void
write_fake_data (IOBUF out, gcry_mpi_t a)
{
  if (a) 
    {
      unsigned int n;
      void *p;
      
      p = gcry_mpi_get_opaque ( a, &n );
      iobuf_write (out, p, (n+7)/8 );
    }
}
Exemplo n.º 6
0
/*
 * Write the mpi A to OUT.
 */
gpg_error_t
gpg_mpi_write (iobuf_t out, gcry_mpi_t a)
{
  int rc;

  if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
    {
      unsigned int nbits;
      const unsigned char *p;
      unsigned char lenhdr[2];

      /* gcry_log_debugmpi ("a", a); */
      p = gcry_mpi_get_opaque (a, &nbits);
      if (p)
        {
          /* Strip leading zero bits.  */
          for (; nbits >= 8 && !*p; p++, nbits -= 8)
            ;
          if (nbits >= 8 && !(*p & 0x80))
            if (--nbits >= 7 && !(*p & 0x40))
              if (--nbits >= 6 && !(*p & 0x20))
                if (--nbits >= 5 && !(*p & 0x10))
                  if (--nbits >= 4 && !(*p & 0x08))
                    if (--nbits >= 3 && !(*p & 0x04))
                      if (--nbits >= 2 && !(*p & 0x02))
                        if (--nbits >= 1 && !(*p & 0x01))
                          --nbits;
        }
      /* gcry_log_debug ("   [%u bit]\n", nbits); */
      /* gcry_log_debughex (" ", p, (nbits+7)/8); */
      lenhdr[0] = nbits >> 8;
      lenhdr[1] = nbits;
      rc = iobuf_write (out, lenhdr, 2);
      if (!rc && p)
        rc = iobuf_write (out, p, (nbits+7)/8);
    }
  else
    {
Exemplo n.º 7
0
static int
do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
{
  int rc = 0;
  int i, nskey, npkey;
  IOBUF a = iobuf_temp(); /* Build in a self-enlarging buffer.  */

  /* Write the version number - if none is specified, use 3 */
  if ( !sk->version )
    iobuf_put ( a, 3 );
  else
    iobuf_put ( a, sk->version );
  write_32 (a, sk->timestamp );

  /* v3 needs the expiration time. */
  if ( sk->version < 4 )
    {
      u16 ndays;
      if ( sk->expiredate )
        ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L);
      else
        ndays = 0;
      write_16(a, ndays);
    }
  
  iobuf_put (a, sk->pubkey_algo );
  
  /* Get number of secret and public parameters.  They are held in one
     array first the public ones, then the secret ones.  */
  nskey = pubkey_get_nskey ( sk->pubkey_algo );
  npkey = pubkey_get_npkey ( sk->pubkey_algo );
  
  /* If we don't have any public parameters - which is the case if we
     don't know the algorithm used - the parameters are stored as one
     blob in a faked (opaque) MPI. */
  if ( !npkey ) 
    {
      write_fake_data( a, sk->skey[0] );
      goto leave;
    }
  assert ( npkey < nskey );

  /* Writing the public parameters is easy. */
  for (i=0; i < npkey; i++ )
    if ((rc = mpi_write (a, sk->skey[i])))
      goto leave;
  
  /* Build the header for protected (encrypted) secret parameters.  */
  if ( sk->is_protected ) 
    {
      if ( is_RSA(sk->pubkey_algo) 
           && sk->version < 4
           && !sk->protect.s2k.mode )
        {
          /* The simple rfc1991 (v3) way. */
          iobuf_put (a, sk->protect.algo );
          iobuf_write (a, sk->protect.iv, sk->protect.ivlen );
	}
      else
        {
          /* OpenPGP protection according to rfc2440. */
          iobuf_put(a, sk->protect.sha1chk? 0xfe : 0xff );
          iobuf_put(a, sk->protect.algo );
          if ( sk->protect.s2k.mode >= 1000 )
            {
              /* These modes are not possible in OpenPGP, we use them
                 to implement our extensions, 101 can be seen as a
                 private/experimental extension (this is not specified
                 in rfc2440 but the same scheme is used for all other
                 algorithm identifiers) */
              iobuf_put(a, 101 ); 
              iobuf_put(a, sk->protect.s2k.hash_algo );
              iobuf_write(a, "GNU", 3 );
              iobuf_put(a, sk->protect.s2k.mode - 1000 );
	    }
          else 
            {
              iobuf_put(a, sk->protect.s2k.mode );
              iobuf_put(a, sk->protect.s2k.hash_algo );
	    }
          if ( sk->protect.s2k.mode == 1
               || sk->protect.s2k.mode == 3 )
            iobuf_write (a, sk->protect.s2k.salt, 8 );

          if ( sk->protect.s2k.mode == 3 )
            iobuf_put (a, sk->protect.s2k.count ); 

          /* For our special modes 1001, 1002 we do not need an IV. */
          if ( sk->protect.s2k.mode != 1001 
               && sk->protect.s2k.mode != 1002 )
            iobuf_write (a, sk->protect.iv, sk->protect.ivlen );
	}
    }
  else
    iobuf_put (a, 0 );

  if ( sk->protect.s2k.mode == 1001 )
    ; /* GnuPG extension - don't write a secret key at all. */ 
  else if ( sk->protect.s2k.mode == 1002 )
    { 
      /* GnuPG extension - divert to OpenPGP smartcard. */ 
      iobuf_put(a, sk->protect.ivlen ); /* Length of the serial number
                                           or 0 for no serial
                                           number. */
      /* The serial number gets stored in the IV field. */
      iobuf_write(a, sk->protect.iv, sk->protect.ivlen);
    }
  else if ( sk->is_protected && sk->version >= 4 )
    {
      /* The secret key is protected - write it out as it is.  */
      byte *p;
      unsigned int ndatabits;
      
      assert (gcry_mpi_get_flag (sk->skey[npkey], GCRYMPI_FLAG_OPAQUE));
      p = gcry_mpi_get_opaque (sk->skey[npkey], &ndatabits );
      iobuf_write (a, p, (ndatabits+7)/8 );
    }
  else if ( sk->is_protected ) 
    {
      /* The secret key is protected the old v4 way. */
      for ( ; i < nskey; i++ ) 
        {
          byte *p;
          unsigned int ndatabits;
          
          assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE));
          p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits);
          iobuf_write (a, p, (ndatabits+7)/8);
        }
      write_16(a, sk->csum );
    }
  else
    {
      /* Non-protected key. */
      for ( ; i < nskey; i++ )
        if ( (rc = mpi_write (a, sk->skey[i])))
          goto leave;
      write_16 (a, sk->csum );
    }

 leave:
  if (!rc)
    {
      /* Build the header of the packet - which we must do after
         writing all the other stuff, so that we know the length of
         the packet */
      write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes);
      /* And finally write it out the real stream */
      rc = iobuf_write_temp( out, a );
    }

  iobuf_close(a); /* Close the remporary buffer */
  return rc;
}
Exemplo n.º 8
0
static int
xxxx_do_check( PKT_secret_key *sk, const char *tryagain_text, int mode,
               int *canceled )
{
    gpg_error_t err;
    byte *buffer;
    u16 csum=0;
    int i, res;
    size_t nbytes;

    if( sk->is_protected ) { /* remove the protection */
	DEK *dek = NULL;
	u32 keyid[4]; /* 4! because we need two of them */
	gcry_cipher_hd_t cipher_hd=NULL;
	PKT_secret_key *save_sk;

	if( sk->protect.s2k.mode == 1001 ) {
	    log_info(_("secret key parts are not available\n"));
	    return GPG_ERR_UNUSABLE_SECKEY;
	}
	if( sk->protect.algo == CIPHER_ALGO_NONE )
	    BUG();
	if( openpgp_cipher_test_algo( sk->protect.algo ) ) {
	    log_info(_("protection algorithm %d%s is not supported\n"),
			sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" );
	    return GPG_ERR_CIPHER_ALGO;
	}
	if(gcry_md_test_algo (sk->protect.s2k.hash_algo))
	  {
	    log_info(_("protection digest %d is not supported\n"),
		     sk->protect.s2k.hash_algo);
	    return GPG_ERR_DIGEST_ALGO;
	  }
	keyid_from_sk( sk, keyid );
	keyid[2] = keyid[3] = 0;
	if (!sk->flags.primary)
          {
            keyid[2] = sk->main_keyid[0];
            keyid[3] = sk->main_keyid[1];
          }
	dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo,
				 &sk->protect.s2k, mode,
                                 tryagain_text, canceled );
        if (!dek && canceled && *canceled)
	    return GPG_ERR_CANCELED;


	err = openpgp_cipher_open (&cipher_hd, sk->protect.algo,
				   GCRY_CIPHER_MODE_CFB,
				   (GCRY_CIPHER_SECURE
				    | (sk->protect.algo >= 100 ?
				       0 : GCRY_CIPHER_ENABLE_SYNC)));
        if (err)
          log_fatal ("cipher open failed: %s\n", gpg_strerror (err) );

	err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen);
        if (err)
          log_fatal ("set key failed: %s\n", gpg_strerror (err) );

	xfree(dek);
	save_sk = copy_secret_key( NULL, sk );

	gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen );

	csum = 0;
	if( sk->version >= 4 ) {
            int ndata;
	    unsigned int ndatabits;
	    byte *p, *data;
            u16 csumc = 0;

	    i = pubkey_get_npkey(sk->pubkey_algo);

            assert ( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE ));
            p = gcry_mpi_get_opaque ( sk->skey[i], &ndatabits );
            ndata = (ndatabits+7)/8;

            if ( ndata > 1 )
              csumc = buf16_to_u16 (p+ndata-2);
	    data = xmalloc_secure ( ndata );
	    gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata );
	    gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ;

	    p = data;
            if (sk->protect.sha1chk) {
                /* This is the new SHA1 checksum method to detect
                   tampering with the key as used by the Klima/Rosa
                   attack */
                sk->csum = 0;
                csum = 1;
                if( ndata < 20 )
                    log_error("not enough bytes for SHA-1 checksum\n");
                else {
                    gcry_md_hd_t h;

                    if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1))
                        BUG(); /* Algo not available. */
                    gcry_md_write (h, data, ndata - 20);
                    gcry_md_final (h);
                    if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1),
                                 data + ndata - 20, 20) )
                      {
                        /* Digest does match.  We have to keep the old
                           style checksum in sk->csum, so that the
                           test used for unprotected keys does work.
                           This test gets used when we are adding new
                           keys. */
                        sk->csum = csum = checksum (data, ndata-20);
                      }
                    gcry_md_close (h);
                }
            }
            else {
                if( ndata < 2 ) {
                    log_error("not enough bytes for checksum\n");
                    sk->csum = 0;
                    csum = 1;
                }
                else {
                    csum = checksum( data, ndata-2);
                    sk->csum = data[ndata-2] << 8 | data[ndata-1];
                    if ( sk->csum != csum ) {
                        /* This is a PGP 7.0.0 workaround */
                        sk->csum = csumc; /* take the encrypted one */
                    }
                }
            }

            /* Must check it here otherwise the mpi_read_xx would fail
               because the length may have an arbitrary value */
            if( sk->csum == csum ) {
                for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
                    if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP,
                                        p, ndata, &nbytes))
                      {
                        /* Checksum was okay, but not correctly
                           decrypted.  */
                        sk->csum = 0;
                        csum = 1;
                        break;
                      }
                    ndata -= nbytes;
                    p += nbytes;
                }
                /* Note: at this point ndata should be 2 for a simple
                   checksum or 20 for the sha1 digest */
            }
	    xfree(data);
	}
	else {
	    for(i=pubkey_get_npkey(sk->pubkey_algo);
		    i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
                byte *p;
                size_t ndata;
                unsigned int ndatabits;

                assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE));
                p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits);
                ndata = (ndatabits+7)/8;
                assert (ndata >= 2);
                assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2);
                buffer = xmalloc_secure (ndata);
		gcry_cipher_sync (cipher_hd);
                buffer[0] = p[0];
                buffer[1] = p[1];
                gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2,
                                     p+2, ndata-2);
                csum += checksum (buffer, ndata);
                gcry_mpi_release (sk->skey[i]);

		err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP,
				     buffer, ndata, &ndata );
		xfree (buffer);
                if (err)
                  {
                    /* Checksum was okay, but not correctly
                       decrypted.  */
                    sk->csum = 0;
                    csum = 1;
                    break;
                  }
/*  		csum += checksum_mpi (sk->skey[i]); */
	    }
	}
	gcry_cipher_close ( cipher_hd );

	/* Now let's see whether we have used the correct passphrase. */
	if( csum != sk->csum ) {
	    copy_secret_key( sk, save_sk );
            passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
	    free_secret_key( save_sk );
	    return gpg_error (GPG_ERR_BAD_PASSPHRASE);
	}

	/* The checksum may fail, so we also check the key itself. */
	res = pk_check_secret_key ( sk->pubkey_algo, sk->skey );
	if( res ) {
	    copy_secret_key( sk, save_sk );
            passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
	    free_secret_key( save_sk );
	    return gpg_error (GPG_ERR_BAD_PASSPHRASE);
	}
	free_secret_key( save_sk );
	sk->is_protected = 0;
    }
    else { /* not protected, assume it is okay if the checksum is okay */
	csum = 0;
	for(i=pubkey_get_npkey(sk->pubkey_algo);
		i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
	    csum += checksum_mpi( sk->skey[i] );
	}
	if( csum != sk->csum )
	    return GPG_ERR_CHECKSUM;
    }

    return 0;
}
Exemplo n.º 9
0
/* Compute an ECDSA signature.
 * Return the signature struct (r,s) from the message hash.  The caller
 * must have allocated R and S.
 */
gpg_err_code_t
_gcry_ecc_ecdsa_sign (gcry_mpi_t input, ECC_secret_key *skey,
                      gcry_mpi_t r, gcry_mpi_t s,
                      int flags, int hashalgo)
{
  gpg_err_code_t err = 0;
  int extraloops = 0;
  gcry_mpi_t k, dr, sum, k_1, x;
  mpi_point_struct I;
  gcry_mpi_t hash;
  const void *abuf;
  unsigned int abits, qbits;
  mpi_ec_t ctx;

  if (DBG_CIPHER)
    log_mpidump ("ecdsa sign hash  ", input );

  qbits = mpi_get_nbits (skey->E.n);

  /* Convert the INPUT into an MPI if needed.  */
  if (mpi_is_opaque (input))
    {
      abuf = gcry_mpi_get_opaque (input, &abits);
      err = gpg_err_code (gcry_mpi_scan (&hash, GCRYMPI_FMT_USG,
                                         abuf, (abits+7)/8, NULL));
      if (err)
        return err;
      if (abits > qbits)
        gcry_mpi_rshift (hash, hash, abits - qbits);
    }
  else
    hash = input;


  k = NULL;
  dr = mpi_alloc (0);
  sum = mpi_alloc (0);
  k_1 = mpi_alloc (0);
  x = mpi_alloc (0);
  point_init (&I);

  ctx = _gcry_mpi_ec_p_internal_new (skey->E.model, skey->E.dialect,
                                     skey->E.p, skey->E.a, skey->E.b);

  /* Two loops to avoid R or S are zero.  This is more of a joke than
     a real demand because the probability of them being zero is less
     than any hardware failure.  Some specs however require it.  */
  do
    {
      do
        {
          mpi_free (k);
          k = NULL;
          if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo)
            {
              /* Use Pornin's method for deterministic DSA.  If this
                 flag is set, it is expected that HASH is an opaque
                 MPI with the to be signed hash.  That hash is also
                 used as h1 from 3.2.a.  */
              if (!mpi_is_opaque (input))
                {
                  err = GPG_ERR_CONFLICT;
                  goto leave;
                }

              abuf = gcry_mpi_get_opaque (input, &abits);
              err = _gcry_dsa_gen_rfc6979_k (&k, skey->E.n, skey->d,
                                             abuf, (abits+7)/8,
                                             hashalgo, extraloops);
              if (err)
                goto leave;
              extraloops++;
            }
          else
            k = _gcry_dsa_gen_k (skey->E.n, GCRY_STRONG_RANDOM);

          _gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx);
          if (_gcry_mpi_ec_get_affine (x, NULL, &I, ctx))
            {
              if (DBG_CIPHER)
                log_debug ("ecc sign: Failed to get affine coordinates\n");
              err = GPG_ERR_BAD_SIGNATURE;
              goto leave;
            }
          mpi_mod (r, x, skey->E.n);  /* r = x mod n */
        }
      while (!mpi_cmp_ui (r, 0));

      mpi_mulm (dr, skey->d, r, skey->E.n); /* dr = d*r mod n  */
      mpi_addm (sum, hash, dr, skey->E.n);  /* sum = hash + (d*r) mod n  */
      mpi_invm (k_1, k, skey->E.n);         /* k_1 = k^(-1) mod n  */
      mpi_mulm (s, k_1, sum, skey->E.n);    /* s = k^(-1)*(hash+(d*r)) mod n */
    }
  while (!mpi_cmp_ui (s, 0));

  if (DBG_CIPHER)
    {
      log_mpidump ("ecdsa sign result r ", r);
      log_mpidump ("ecdsa sign result s ", s);
    }

 leave:
  _gcry_mpi_ec_free (ctx);
  point_free (&I);
  mpi_free (x);
  mpi_free (k_1);
  mpi_free (sum);
  mpi_free (dr);
  mpi_free (k);

  if (hash != input)
    mpi_free (hash);

  return err;
}
Exemplo n.º 10
0
/****************
 * Scan the provided buffer and return the S expression in our internal
 * format.  Returns a newly allocated expression.  If erroff is not NULL and
 * a parsing error has occurred, the offset into buffer will be returned.
 * If ARGFLAG is true, the function supports some printf like
 * expressions.
 *  These are:
 *	%m - MPI
 *	%s - string (no autoswitch to secure allocation)
 *	%d - integer stored as string (no autoswitch to secure allocation)
 *      %b - memory buffer; this takes _two_ arguments: an integer with the
 *           length of the buffer and a pointer to the buffer.
 *      %S - Copy an gcry_sexp_t here.  The S-expression needs to be a
 *           regular one, starting with a parenthesis.
 *           (no autoswitch to secure allocation)
 *  all other format elements are currently not defined and return an error.
 *  this includes the "%%" sequence becauce the percent sign is not an
 *  allowed character.
 * FIXME: We should find a way to store the secure-MPIs not in the string
 * but as reference to somewhere - this can help us to save huge amounts
 * of secure memory.  The problem is, that if only one element is secure, all
 * other elements are automagicaly copied to secure memory too, so the most
 * common operation gcry_sexp_cdr_mpi() will always return a secure MPI
 * regardless whether it is needed or not.
 */
static gcry_error_t
vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
	     const char *buffer, size_t length, int argflag,
	     void **arg_list, va_list arg_ptr)
{
  gcry_err_code_t err = 0;
  static const char tokenchars[] =
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "0123456789-./_:*+=";
  const char *p;
  size_t n;
  const char *digptr = NULL;
  const char *quoted = NULL;
  const char *tokenp = NULL;
  const char *hexfmt = NULL;
  const char *base64 = NULL;
  const char *disphint = NULL;
  const char *percent = NULL;
  int hexcount = 0;
  int quoted_esc = 0;
  int datalen = 0;
  size_t dummy_erroff;
  struct make_space_ctx c;
  int arg_counter = 0;
  int level = 0;

  if (!erroff)
    erroff = &dummy_erroff;

  /* Depending on whether ARG_LIST is non-zero or not, this macro gives
     us the next argument, either from the variable argument list as
     specified by ARG_PTR or from the argument array ARG_LIST.  */
#define ARG_NEXT(storage, type)                          \
  do                                                     \
    {                                                    \
      if (!arg_list)                                     \
	storage = va_arg (arg_ptr, type);                \
      else                                               \
	storage = *((type *) (arg_list[arg_counter++])); \
    }                                                    \
  while (0)

  /* The MAKE_SPACE macro is used before each store operation to
     ensure that the buffer is large enough.  It requires a global
     context named C and jumps out to the label LEAVE on error! It
     also sets ERROFF using the variables BUFFER and P.  */
#define MAKE_SPACE(n)  do {                                                \
                            gpg_err_code_t _ms_err = make_space (&c, (n)); \
                            if (_ms_err)                                   \
                              {                                            \
                                err = _ms_err;                             \
                                *erroff = p - buffer;                      \
                                goto leave;                                \
                              }                                            \
                       } while (0)

  /* The STORE_LEN macro is used to store the length N at buffer P. */
#define STORE_LEN(p,n) do {						   \
			    DATALEN ashort = (n);			   \
			    memcpy ( (p), &ashort, sizeof(ashort) );	   \
			    (p) += sizeof (ashort);			   \
			} while (0)

  /* We assume that the internal representation takes less memory than
     the provided one.  However, we add space for one extra datalen so
     that the code which does the ST_CLOSE can use MAKE_SPACE */
  c.allocated = length + sizeof(DATALEN);
  if (buffer && length && gcry_is_secure (buffer))
    c.sexp = gcry_malloc_secure (sizeof *c.sexp + c.allocated - 1);
  else
    c.sexp = gcry_malloc (sizeof *c.sexp + c.allocated - 1);
  if (!c.sexp)
    {
      err = gpg_err_code_from_errno (errno);
      *erroff = 0;
      goto leave;
    }
  c.pos = c.sexp->d;

  for (p = buffer, n = length; n; p++, n--)
    {
      if (tokenp && !hexfmt)
	{
	  if (strchr (tokenchars, *p))
	    continue;
	  else
	    {
	      datalen = p - tokenp;
	      MAKE_SPACE (datalen);
	      *c.pos++ = ST_DATA;
	      STORE_LEN (c.pos, datalen);
	      memcpy (c.pos, tokenp, datalen);
	      c.pos += datalen;
	      tokenp = NULL;
	    }
	}

      if (quoted)
	{
	  if (quoted_esc)
	    {
	      switch (*p)
		{
		case 'b': case 't': case 'v': case 'n': case 'f':
		case 'r': case '"': case '\'': case '\\':
		  quoted_esc = 0;
		  break;

		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7':
		  if (!((n > 2)
                        && (p[1] >= '0') && (p[1] <= '7')
                        && (p[2] >= '0') && (p[2] <= '7')))
		    {
		      *erroff = p - buffer;
		      /* Invalid octal value.  */
		      err = GPG_ERR_SEXP_BAD_QUOTATION;
                      goto leave;
		    }
		  p += 2;
		  n -= 2;
		  quoted_esc = 0;
		  break;

		case 'x':
		  if (!((n > 2) && hexdigitp (p+1) && hexdigitp (p+2)))
		    {
		      *erroff = p - buffer;
		      /* Invalid hex value.  */
		      err = GPG_ERR_SEXP_BAD_QUOTATION;
                      goto leave;
		    }
		  p += 2;
		  n -= 2;
		  quoted_esc = 0;
		  break;

		case '\r':
		  /* ignore CR[,LF] */
		  if (n && (p[1] == '\n'))
		    {
		      p++;
		      n--;
		    }
		  quoted_esc = 0;
		  break;

		case '\n':
		  /* ignore LF[,CR] */
		  if (n && (p[1] == '\r'))
		    {
		      p++;
		      n--;
		    }
		  quoted_esc = 0;
		  break;

		default:
		  *erroff = p - buffer;
		  /* Invalid quoted string escape.  */
		  err = GPG_ERR_SEXP_BAD_QUOTATION;
                  goto leave;
		}
	    }
	  else if (*p == '\\')
	    quoted_esc = 1;
	  else if (*p == '\"')
	    {
	      /* Keep it easy - we know that the unquoted string will
		 never be larger. */
	      unsigned char *save;
	      size_t len;

	      quoted++; /* Skip leading quote.  */
	      MAKE_SPACE (p - quoted);
	      *c.pos++ = ST_DATA;
	      save = c.pos;
	      STORE_LEN (c.pos, 0); /* Will be fixed up later.  */
	      len = unquote_string (quoted, p - quoted, c.pos);
	      c.pos += len;
	      STORE_LEN (save, len);
	      quoted = NULL;
	    }
	}
      else if (hexfmt)
	{
	  if (isxdigit (*p))
	    hexcount++;
	  else if (*p == '#')
	    {
	      if ((hexcount & 1))
		{
		  *erroff = p - buffer;
		  err = GPG_ERR_SEXP_ODD_HEX_NUMBERS;
                  goto leave;
		}

	      datalen = hexcount / 2;
	      MAKE_SPACE (datalen);
	      *c.pos++ = ST_DATA;
	      STORE_LEN (c.pos, datalen);
	      for (hexfmt++; hexfmt < p; hexfmt++)
		{
                  int tmpc;

		  if (whitespacep (hexfmt))
		    continue;
		  tmpc = hextonibble (*(const unsigned char*)hexfmt);
                  for (hexfmt++; hexfmt < p && whitespacep (hexfmt); hexfmt++)
		    ;
                  if (hexfmt < p)
                    {
                      tmpc *= 16;
                      tmpc += hextonibble (*(const unsigned char*)hexfmt);
                    }
                  *c.pos++ = tmpc;
		}
	      hexfmt = NULL;
	    }
	  else if (!whitespacep (p))
	    {
	      *erroff = p - buffer;
	      err = GPG_ERR_SEXP_BAD_HEX_CHAR;
              goto leave;
	    }
	}
      else if (base64)
	{
	  if (*p == '|')
	    base64 = NULL;
	}
      else if (digptr)
	{
	  if (digitp (p))
	    ;
	  else if (*p == ':')
	    {
	      datalen = atoi (digptr); /* FIXME: check for overflow.  */
	      digptr = NULL;
	      if (datalen > n - 1)
		{
		  *erroff = p - buffer;
		  /* Buffer too short.  */
		  err = GPG_ERR_SEXP_STRING_TOO_LONG;
                  goto leave;
		}
	      /* Make a new list entry.  */
	      MAKE_SPACE (datalen);
	      *c.pos++ = ST_DATA;
	      STORE_LEN (c.pos, datalen);
	      memcpy (c.pos, p + 1, datalen);
	      c.pos += datalen;
	      n -= datalen;
	      p += datalen;
	    }
	  else if (*p == '\"')
	    {
	      digptr = NULL; /* We ignore the optional length.  */
	      quoted = p;
	      quoted_esc = 0;
	    }
	  else if (*p == '#')
	    {
	      digptr = NULL; /* We ignore the optional length.  */
	      hexfmt = p;
	      hexcount = 0;
	    }
	  else if (*p == '|')
	    {
	      digptr = NULL; /* We ignore the optional length.  */
	      base64 = p;
	    }
	  else
	    {
	      *erroff = p - buffer;
	      err = GPG_ERR_SEXP_INV_LEN_SPEC;
              goto leave;
	    }
	}
      else if (percent)
	{
	  if (*p == 'm' || *p == 'M')
	    {
	      /* Insert an MPI.  */
	      gcry_mpi_t m;
	      size_t nm = 0;
              int mpifmt = *p == 'm'? GCRYMPI_FMT_STD: GCRYMPI_FMT_USG;

	      ARG_NEXT (m, gcry_mpi_t);

              if (gcry_mpi_get_flag (m, GCRYMPI_FLAG_OPAQUE))
                {
                  void *mp;
                  unsigned int nbits;

                  mp = gcry_mpi_get_opaque (m, &nbits);
                  nm = (nbits+7)/8;
                  if (mp && nm)
                    {
                      MAKE_SPACE (nm);
                      if (!gcry_is_secure (c.sexp->d)
                          && gcry_mpi_get_flag (m, GCRYMPI_FLAG_SECURE))
                        {
                          /* We have to switch to secure allocation.  */
                          gcry_sexp_t newsexp;
                          byte *newhead;

                          newsexp = gcry_malloc_secure (sizeof *newsexp
                                                        + c.allocated - 1);
                          if (!newsexp)
                            {
                              err = gpg_err_code_from_errno (errno);
                              goto leave;
                            }
                          newhead = newsexp->d;
                          memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
                          c.pos = newhead + (c.pos - c.sexp->d);
                          gcry_free (c.sexp);
                          c.sexp = newsexp;
                        }

                      *c.pos++ = ST_DATA;
                      STORE_LEN (c.pos, nm);
                      memcpy (c.pos, mp, nm);
                      c.pos += nm;
                    }
                }
              else
                {
                  if (gcry_mpi_print (mpifmt, NULL, 0, &nm, m))
                    BUG ();

                  MAKE_SPACE (nm);
                  if (!gcry_is_secure (c.sexp->d)
                      && gcry_mpi_get_flag ( m, GCRYMPI_FLAG_SECURE))
                    {
                      /* We have to switch to secure allocation.  */
                      gcry_sexp_t newsexp;
                      byte *newhead;

                      newsexp = gcry_malloc_secure (sizeof *newsexp
                                                    + c.allocated - 1);
                      if (!newsexp)
                        {
                          err = gpg_err_code_from_errno (errno);
                          goto leave;
                        }
                      newhead = newsexp->d;
                      memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
                      c.pos = newhead + (c.pos - c.sexp->d);
                      gcry_free (c.sexp);
                      c.sexp = newsexp;
                    }

                  *c.pos++ = ST_DATA;
                  STORE_LEN (c.pos, nm);
                  if (gcry_mpi_print (mpifmt, c.pos, nm, &nm, m))
                    BUG ();
                  c.pos += nm;
                }
	    }
	  else if (*p == 's')
	    {
	      /* Insert an string.  */
	      const char *astr;
	      size_t alen;

	      ARG_NEXT (astr, const char *);
	      alen = strlen (astr);

	      MAKE_SPACE (alen);
	      *c.pos++ = ST_DATA;
	      STORE_LEN (c.pos, alen);
	      memcpy (c.pos, astr, alen);
	      c.pos += alen;
	    }
	  else if (*p == 'b')
	    {
	      /* Insert a memory buffer.  */
	      const char *astr;
	      int alen;

	      ARG_NEXT (alen, int);
	      ARG_NEXT (astr, const char *);

	      MAKE_SPACE (alen);
	      if (alen
                  && !gcry_is_secure (c.sexp->d)
		  && gcry_is_secure (astr))
              {
		  /* We have to switch to secure allocation.  */
		  gcry_sexp_t newsexp;
		  byte *newhead;

		  newsexp = gcry_malloc_secure (sizeof *newsexp
                                                + c.allocated - 1);
                  if (!newsexp)
                    {
                      err = gpg_err_code_from_errno (errno);
                      goto leave;
                    }
		  newhead = newsexp->d;
		  memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
		  c.pos = newhead + (c.pos - c.sexp->d);
		  gcry_free (c.sexp);
		  c.sexp = newsexp;
		}

	      *c.pos++ = ST_DATA;
	      STORE_LEN (c.pos, alen);
	      memcpy (c.pos, astr, alen);
	      c.pos += alen;
	    }
	  else if (*p == 'd')
	    {
	      /* Insert an integer as string.  */
	      int aint;
	      size_t alen;
	      char buf[35];

	      ARG_NEXT (aint, int);
	      sprintf (buf, "%d", aint);
	      alen = strlen (buf);
	      MAKE_SPACE (alen);
	      *c.pos++ = ST_DATA;
	      STORE_LEN (c.pos, alen);
	      memcpy (c.pos, buf, alen);
	      c.pos += alen;
	    }
	  else if (*p == 'u')
	    {
	      /* Insert an unsigned integer as string.  */
	      unsigned int aint;
	      size_t alen;
	      char buf[35];

	      ARG_NEXT (aint, unsigned int);
	      sprintf (buf, "%u", aint);
	      alen = strlen (buf);
	      MAKE_SPACE (alen);
	      *c.pos++ = ST_DATA;
	      STORE_LEN (c.pos, alen);
	      memcpy (c.pos, buf, alen);
	      c.pos += alen;
	    }
	  else if (*p == 'S')
	    {
	      /* Insert a gcry_sexp_t.  */
	      gcry_sexp_t asexp;
	      size_t alen, aoff;

	      ARG_NEXT (asexp, gcry_sexp_t);
              alen = get_internal_buffer (asexp, &aoff);
              if (alen)
                {
                  MAKE_SPACE (alen);
                  memcpy (c.pos, asexp->d + aoff, alen);
                  c.pos += alen;
                }
	    }
	  else
	    {
	      *erroff = p - buffer;
	      /* Invalid format specifier.  */
	      err = GPG_ERR_SEXP_INV_LEN_SPEC;
              goto leave;
	    }
	  percent = NULL;
	}
      else if (*p == '(')
	{
	  if (disphint)
	    {
	      *erroff = p - buffer;
	      /* Open display hint.  */
	      err = GPG_ERR_SEXP_UNMATCHED_DH;
              goto leave;
	    }
	  MAKE_SPACE (0);
	  *c.pos++ = ST_OPEN;
	  level++;
	}
      else if (*p == ')')
	{
	  /* Walk up.  */
	  if (disphint)
	    {
	      *erroff = p - buffer;
	      /* Open display hint.  */
	      err = GPG_ERR_SEXP_UNMATCHED_DH;
              goto leave;
	    }
	  MAKE_SPACE (0);
	  *c.pos++ = ST_CLOSE;
	  level--;
	}
      else if (*p == '\"')
	{
	  quoted = p;
	  quoted_esc = 0;
	}
      else if (*p == '#')
	{
	  hexfmt = p;
	  hexcount = 0;
	}
      else if (*p == '|')
	base64 = p;
      else if (*p == '[')
	{
	  if (disphint)
	    {
	      *erroff = p - buffer;
	      /* Open display hint.  */
	      err = GPG_ERR_SEXP_NESTED_DH;
              goto leave;
	    }
	  disphint = p;
	}
      else if (*p == ']')
	{
	  if (!disphint)
	    {
	      *erroff = p - buffer;
	      /* Open display hint.  */
	      err = GPG_ERR_SEXP_UNMATCHED_DH;
              goto leave;
	    }
	  disphint = NULL;
	}
      else if (digitp (p))
	{
	  if (*p == '0')
	    {
	      /* A length may not begin with zero.  */
	      *erroff = p - buffer;
	      err = GPG_ERR_SEXP_ZERO_PREFIX;
              goto leave;
	    }
	  digptr = p;
	}
      else if (strchr (tokenchars, *p))
	tokenp = p;
      else if (whitespacep (p))
	;
      else if (*p == '{')
	{
	  /* fixme: handle rescanning: we can do this by saving our
	     current state and start over at p+1 -- Hmmm. At this
	     point here we are in a well defined state, so we don't
	     need to save it.  Great.  */
	  *erroff = p - buffer;
	  err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
          goto leave;
	}
      else if (strchr ("&\\", *p))
	{
	  /* Reserved punctuation.  */
	  *erroff = p - buffer;
	  err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
          goto leave;
	}
      else if (argflag && (*p == '%'))
	percent = p;
      else
	{
	  /* Bad or unavailable.  */
	  *erroff = p - buffer;
	  err = GPG_ERR_SEXP_BAD_CHARACTER;
          goto leave;
	}
    }
  MAKE_SPACE (0);
  *c.pos++ = ST_STOP;

  if (level && !err)
    err = GPG_ERR_SEXP_UNMATCHED_PAREN;

 leave:
  if (err)
    {
      /* Error -> deallocate.  */
      if (c.sexp)
        {
          /* Extra paranoid wipe on error. */
          if (gcry_is_secure (c.sexp))
            wipememory (c.sexp, sizeof (struct gcry_sexp) + c.allocated - 1);
          gcry_free (c.sexp);
        }
      /* This might be expected by existing code...  */
      *retsexp = NULL;
    }
  else
    *retsexp = normalize (c.sexp);

  return gcry_error (err);
#undef MAKE_SPACE
#undef STORE_LEN
}