parse_key (const unsigned char *data, size_t datalen,
           struct _keybox_openpgp_key_info *ki)
{
  gpg_error_t err;
  const unsigned char *data_start = data;
  int i, version, algorithm;
  size_t n;
  int npkey;
  unsigned char hashbuffer[768];
  const unsigned char *mpi_n = NULL;
  size_t mpi_n_len = 0, mpi_e_len = 0;
  gcry_md_hd_t md;
  int is_ecc = 0;

  if (datalen < 5)
    return gpg_error (GPG_ERR_INV_PACKET);
  version = *data++; datalen--;
  if (version < 2 || version > 4 )
    return gpg_error (GPG_ERR_INV_PACKET); /* Invalid version. */

  /*timestamp = ((data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]));*/
  data +=4; datalen -=4;

  if (version < 4)
    {
      if (datalen < 2)
        return gpg_error (GPG_ERR_INV_PACKET);
      data +=2; datalen -= 2;
    }

  if (!datalen)
    return gpg_error (GPG_ERR_INV_PACKET);
  algorithm = *data++; datalen--;

  switch (algorithm)
    {
    case PUBKEY_ALGO_RSA:
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_RSA_S:
      npkey = 2;
      break;
    case PUBKEY_ALGO_ELGAMAL_E:
    case PUBKEY_ALGO_ELGAMAL:
      npkey = 3;
      break;
    case PUBKEY_ALGO_DSA:
      npkey = 4;
      break;
    case PUBKEY_ALGO_ECDH:
      npkey = 3;
      is_ecc = 1;
      break;
    case PUBKEY_ALGO_ECDSA:
    case PUBKEY_ALGO_EDDSA:
      npkey = 2;
      is_ecc = 1;
      break;
    default: /* Unknown algorithm. */
      return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
    }

  ki->algo = algorithm;

  for (i=0; i < npkey; i++ )
    {
      unsigned int nbits, nbytes;

      if (datalen < 2)
        return gpg_error (GPG_ERR_INV_PACKET);

      if (is_ecc && (i == 0 || i == 2))
        {
          nbytes = data[0];
          if (nbytes < 2 || nbytes > 254)
            return gpg_error (GPG_ERR_INV_PACKET);
          nbytes++; /* The size byte itself.  */
          if (datalen < nbytes)
            return gpg_error (GPG_ERR_INV_PACKET);
        }
      else
        {
          nbits = ((data[0]<<8)|(data[1]));
          data += 2;
          datalen -= 2;
          nbytes = (nbits+7) / 8;
          if (datalen < nbytes)
            return gpg_error (GPG_ERR_INV_PACKET);
          /* For use by v3 fingerprint calculation we need to know the RSA
             modulus and exponent. */
          if (i==0)
            {
              mpi_n = data;
              mpi_n_len = nbytes;
            }
          else if (i==1)
            mpi_e_len = nbytes;
        }

      data += nbytes; datalen -= nbytes;
    }
  n = data - data_start;

  if (version < 4)
    {
      /* We do not support any other algorithm than RSA in v3
         packets. */
      if (algorithm < 1 || algorithm > 3)
        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);

      err = gcry_md_open (&md, GCRY_MD_MD5, 0);
      if (err)
        return err; /* Oops */
      gcry_md_write (md, mpi_n, mpi_n_len);
      gcry_md_write (md, mpi_n+mpi_n_len+2, mpi_e_len);
      memcpy (ki->fpr, gcry_md_read (md, 0), 16);
      gcry_md_close (md);
      ki->fprlen = 16;

      if (mpi_n_len < 8)
        {
          /* Moduli less than 64 bit are out of the specs scope.  Zero
             them out because this is what gpg does too. */
          memset (ki->keyid, 0, 8);
        }
      else
        memcpy (ki->keyid, mpi_n + mpi_n_len - 8, 8);
    }
  else
    {
      /* Its a pitty that we need to prefix the buffer with the tag
         and a length header: We can't simply pass it to the fast
         hashing function for that reason.  It might be a good idea to
         have a scatter-gather enabled hash function. What we do here
         is to use a static buffer if this one is large enough and
         only use the regular hash functions if this buffer is not
         large enough. */
      if ( 3 + n < sizeof hashbuffer )
        {
          hashbuffer[0] = 0x99;     /* CTB */
          hashbuffer[1] = (n >> 8); /* 2 byte length header. */
          hashbuffer[2] = n;
          memcpy (hashbuffer + 3, data_start, n);
          gcry_md_hash_buffer (GCRY_MD_SHA1, ki->fpr, hashbuffer, 3 + n);
        }
      else
        {
          err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
          if (err)
            return err; /* Oops */
          gcry_md_putc (md, 0x99 );     /* CTB */
          gcry_md_putc (md, (n >> 8) ); /* 2 byte length header. */
          gcry_md_putc (md, n );
          gcry_md_write (md, data_start, n);
          memcpy (ki->fpr, gcry_md_read (md, 0), 20);
          gcry_md_close (md);
        }
      ki->fprlen = 20;
      memcpy (ki->keyid, ki->fpr+12, 8);
    }

  return 0;
}