_keybox_dump_blob (KEYBOXBLOB blob, FILE *fp)
{
  const byte *buffer;
  size_t length;
  int type, i;
  ulong n, nkeys, keyinfolen;
  ulong nuids, uidinfolen;
  ulong nsigs, siginfolen;
  ulong rawdata_off, rawdata_len;
  ulong nserial;
  ulong unhashed;
  const byte *p;

  buffer = _keybox_get_blob_image (blob, &length);

  if (length < 32)
    {
      fprintf (fp, "[blob too short]\n");
      return -1;
    }

  n = get32( buffer );
  if (n > length)
    fprintf (fp, "[blob larger than length - output truncated]\n");
  else
    length = n;  /* ignore the rest */

  fprintf (fp, "Length: %lu\n", n );
  type = buffer[4];
  switch (type)
    {
    case KEYBOX_BLOBTYPE_EMPTY:
      fprintf (fp, "Type:   Empty\n");
      return 0;

    case KEYBOX_BLOBTYPE_HEADER:
      fprintf (fp, "Type:   Header\n");
      return dump_header_blob (buffer, length, fp);
    case KEYBOX_BLOBTYPE_PGP:
      fprintf (fp, "Type:   OpenPGP\n");
      break;
    case KEYBOX_BLOBTYPE_X509:
      fprintf (fp, "Type:   X.509\n");
      break;
    default:
      fprintf (fp, "Type:   %d\n", type);
      fprintf (fp, "[can't dump this blob type]\n");
      return 0;
    }
  fprintf (fp, "Version: %d\n", buffer[5]);

  if (length < 40)
    {
      fprintf (fp, "[blob too short]\n");
      return -1;
    }

  n = get16 (buffer + 6);
  fprintf( fp, "Blob-Flags: %04lX", n);
  if (n)
    {
      int any = 0;

      fputs (" (", fp);
      if ((n & 1))
        {
          fputs ("secret", fp);
          any++;
        }
      if ((n & 2))
        {
          if (any)
            putc (',', fp);
          fputs ("ephemeral", fp);
          any++;
        }
      putc (')', fp);
    }
  putc ('\n', fp);

  rawdata_off = get32 (buffer + 8);
  rawdata_len = get32 (buffer + 12);

  fprintf( fp, "Data-Offset: %lu\n", rawdata_off );
  fprintf( fp, "Data-Length: %lu\n", rawdata_len );
  if (rawdata_off > length || rawdata_len > length
      || rawdata_off+rawdata_len > length
      || rawdata_len + 4 > length
      || rawdata_off+rawdata_len + 4 > length)
    fprintf (fp, "[Error: raw data larger than blob]\n");
  unhashed = length - rawdata_off - rawdata_len;
  fprintf (fp, "Unhashed: %lu\n", unhashed);

  nkeys = get16 (buffer + 16);
  fprintf (fp, "Key-Count: %lu\n", nkeys );
  if (!nkeys)
    fprintf (fp, "[Error: no keys]\n");
  if (nkeys > 1 && type == KEYBOX_BLOBTYPE_X509)
    fprintf (fp, "[Error: only one key allowed for X509]\n");

  keyinfolen = get16 (buffer + 18 );
  fprintf (fp, "Key-Info-Length: %lu\n", keyinfolen);
  /* fixme: check bounds */
  p = buffer + 20;
  for (n=0; n < nkeys; n++, p += keyinfolen)
    {
      ulong kidoff, kflags;

      fprintf (fp, "Key-Fpr[%lu]: ", n );
      for (i=0; i < 20; i++ )
        fprintf (fp, "%02X", p[i]);
      kidoff = get32 (p + 20);
      fprintf (fp, "\nKey-Kid-Off[%lu]: %lu\n", n, kidoff );
      fprintf (fp, "Key-Kid[%lu]: ", n );
      /* fixme: check bounds */
      for (i=0; i < 8; i++ )
        fprintf (fp, "%02X", buffer[kidoff+i] );
      kflags = get16 (p + 24 );
      fprintf( fp, "\nKey-Flags[%lu]: %04lX\n", n, kflags);
    }

  /* serial number */
  fputs ("Serial-No: ", fp);
  nserial = get16 (p);
  p += 2;
  if (!nserial)
    fputs ("none", fp);
  else
    {
      for (; nserial; nserial--, p++)
        fprintf (fp, "%02X", *p);
    }
  putc ('\n', fp);

  /* user IDs */
  nuids = get16 (p);
  fprintf (fp, "Uid-Count: %lu\n", nuids );
  uidinfolen = get16  (p + 2);
  fprintf (fp, "Uid-Info-Length: %lu\n", uidinfolen);
  /* fixme: check bounds */
  p += 4;
  for (n=0; n < nuids; n++, p += uidinfolen)
    {
      ulong uidoff, uidlen, uflags;

      uidoff = get32( p );
      uidlen = get32( p+4 );
      if (type == KEYBOX_BLOBTYPE_X509 && !n)
        {
          fprintf (fp, "Issuer-Off: %lu\n", uidoff );
          fprintf (fp, "Issuer-Len: %lu\n", uidlen );
          fprintf (fp, "Issuer: \"");
        }
      else if (type == KEYBOX_BLOBTYPE_X509 && n == 1)
        {
          fprintf (fp, "Subject-Off: %lu\n", uidoff );
          fprintf (fp, "Subject-Len: %lu\n", uidlen );
          fprintf (fp, "Subject: \"");
        }
      else
        {
          fprintf (fp, "Uid-Off[%lu]: %lu\n", n, uidoff );
          fprintf (fp, "Uid-Len[%lu]: %lu\n", n, uidlen );
          fprintf (fp, "Uid[%lu]: \"", n );
        }
      print_string (fp, buffer+uidoff, uidlen, '\"');
      fputs ("\"\n", fp);
      uflags = get16 (p + 8);
      if (type == KEYBOX_BLOBTYPE_X509 && !n)
        {
          fprintf (fp, "Issuer-Flags: %04lX\n", uflags );
          fprintf (fp, "Issuer-Validity: %d\n", p[10] );
        }
      else if (type == KEYBOX_BLOBTYPE_X509 && n == 1)
        {
          fprintf (fp, "Subject-Flags: %04lX\n", uflags );
          fprintf (fp, "Subject-Validity: %d\n", p[10] );
        }
      else
        {
          fprintf (fp, "Uid-Flags[%lu]: %04lX\n", n, uflags );
          fprintf (fp, "Uid-Validity[%lu]: %d\n", n, p[10] );
        }
    }

  nsigs = get16 (p);
  fprintf (fp, "Sig-Count: %lu\n", nsigs );
  siginfolen = get16 (p + 2);
  fprintf (fp, "Sig-Info-Length: %lu\n", siginfolen );
  /* fixme: check bounds  */
  p += 4;
  {
    int in_range = 0;
    ulong first = 0;

    for (n=0; n < nsigs; n++, p += siginfolen)
      {
        ulong sflags;

        sflags = get32 (p);
        if (!in_range && !sflags)
          {
            in_range = 1;
            first = n;
            continue;
          }
        if (in_range && !sflags)
          continue;
        if (in_range)
          {
            fprintf (fp, "Sig-Expire[%lu-%lu]: [not checked]\n", first, n-1);
            in_range = 0;
          }

        fprintf (fp, "Sig-Expire[%lu]: ", n );
        if (!sflags)
          fputs ("[not checked]", fp);
        else if (sflags == 1 )
          fputs ("[missing key]", fp);
        else if (sflags == 2 )
          fputs ("[bad signature]", fp);
        else if (sflags < 0x10000000)
          fprintf (fp, "[bad flag %0lx]", sflags);
        else if (sflags == (ulong)(-1))
          fputs ("[good - does not expire]", fp );
        else
          fprintf (fp, "[good - expires at %lu]", sflags);
        putc ('\n', fp );
      }
    if (in_range)
      {
        fprintf (fp, "Sig-Expire[%lu-%lu]: [not checked]\n", first, n-1);
        in_range = 0;
      }
  }
  fprintf (fp, "Ownertrust: %d\n", p[0] );
  fprintf (fp, "All-Validity: %d\n", p[1] );
  p += 4;
  n = get32 (p); p += 4;
  fprintf (fp, "Recheck-After: %lu\n", n );
  n = get32 (p ); p += 4;
  fprintf( fp, "Latest-Timestamp: %lu\n", n );
  n = get32 (p ); p += 4;
  fprintf (fp, "Created-At: %lu\n", n );
  n = get32 (p ); p += 4;
  fprintf (fp, "Reserved-Space: %lu\n", n );

  if (n >= 4 && unhashed >= 24)
    {
      n = get32 ( buffer + length - unhashed);
      fprintf (fp, "Storage-Flags: %08lx\n", n );
    }
  print_checksum (buffer, length, unhashed, fp);
  return 0;
}