Exemple #1
0
gpgme_verify_result_t
gpgme_op_verify_result (gpgme_ctx_t ctx)
{
  void *hook;
  op_data_t opd;
  gpgme_error_t err;

  TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx);
  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
  opd = hook;
  if (err || !opd)
    {
      TRACE_SUC0 ("result=(null)");
      return NULL;
    }

  if (_gpgme_debug_trace ())
    {
      gpgme_signature_t sig = opd->result.signatures;
      int i = 0;

      while (sig)
	{
	  TRACE_LOG4 ("sig[%i] = fpr %s, summary 0x%x, status %s",
		      i, sig->fpr, sig->summary, gpg_strerror (sig->status));
	  TRACE_LOG6 ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s",
		      i, sig->timestamp, sig->exp_timestamp,
		      sig->wrong_key_usage ? "wrong key usage" : "",
		      sig->pka_trust == 1 ? "pka bad"
		      : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"),
		      sig->chain_model ? "chain model" : "");
	  TRACE_LOG5 ("sig[%i] = validity 0x%x (%s), algos %s/%s",
		      i, sig->validity, gpg_strerror (sig->validity_reason),
		      gpgme_pubkey_algo_name (sig->pubkey_algo),
		      gpgme_hash_algo_name (sig->hash_algo));
	  if (sig->pka_address)
	    {
	      TRACE_LOG2 ("sig[%i] = PKA address %s", i, sig->pka_address);
	    }
	  if (sig->notations)
	    {
	      TRACE_LOG1 ("sig[%i] = has notations (not shown)", i);
	    }	  
	  sig = sig->next;
	  i++;
	}
    }

  TRACE_SUC1 ("result=%p", &opd->result);
  return &opd->result;
}
static char * geanypg_result(gpgme_signature_t sig)
{
    char format[] =
    "status ....: %s\n"
    "summary ...:%s\n"
    "fingerprint: %s\n"
    "created ...: %s"
    "expires ...: %s"
    "validity ..: %s\n"
    "val.reason : %s\n"
    "pubkey algo: %s\n"
    "digest algo: %s\n"
    "pka address: %s\n"
    "pka trust .: %s\n"
    "other flags:%s%s\n"
    "notations .: %s\n"; // 210 characters
    char * buffer = (char *)calloc(2048, 1); // everything together probably won't be more
                                          // than 1061 characters, but we don't want to
                                          // take risks
    char summary[128];
    const char * pubkey = gpgme_pubkey_algo_name(sig->pubkey_algo);
    const char * hash = gpgme_hash_algo_name(sig->hash_algo);
    char created[64];
    char expires[64];
    if (sig->timestamp)
        strncpy(created, ctime((time_t*)&sig->timestamp), 64);
    else
        strcpy(created, "Unknown\n");

    if (sig->exp_timestamp)
        strncpy(expires, ctime((time_t*)&sig->exp_timestamp), 64);
    else
        strcpy(expires, "Unknown\n");

    memset(summary, 0, 128);
    sprintf(buffer, format,
        gpgme_strerror(sig->status),            // probably won't be more than 128
        geanypg_summary(sig->summary, summary), // max 105 characters
        sig->fpr ? sig->fpr : "[None]",         // max 40 characters
        created,                                // probably about 24 characters
        expires,                                // probably about 24 characters
        geanypg_validity(sig->validity),        // max 11 characters
        gpgme_strerror(sig->status),            // probably won't be more than 128
        pubkey ? pubkey : "Unknown",            // probably won't be more than 32
        hash ? hash : "Unknown",            // probably won't be more than 32
        sig->pka_address ? sig->pka_address : "[None]", // probably won't be more than 128
        sig->pka_trust == 0 ? "n/a" : sig->pka_trust == 1 ? "bad" : sig->pka_trust == 2 ? "okay": "RFU", // max 4 characters
        sig->wrong_key_usage ? " wrong-key-usage" : "", sig->chain_model ? " chain-model" : "",          // max 28 characters
        sig->notations ? "yes" : "no");         // max 3 characters
    return buffer;
}
Exemple #3
0
static int
pkcs7_sign (GMimeCryptoContext *context, const char *userid, GMimeDigestAlgo digest,
	    GMimeStream *istream, GMimeStream *ostream, GError **err)
{
#ifdef ENABLE_SMIME
	GMimePkcs7Context *ctx = (GMimePkcs7Context *) context;
	Pkcs7Ctx *pkcs7 = ctx->priv;
	gpgme_sign_result_t result;
	gpgme_data_t input, output;
	gpgme_error_t error;
	
	if (!pkcs7_add_signer (pkcs7, userid, err))
		return -1;
	
	gpgme_set_armor (pkcs7->ctx, FALSE);
	
	if ((error = gpgme_data_new_from_cbs (&input, &pkcs7_stream_funcs, istream)) != GPG_ERR_NO_ERROR) {
		g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open input stream"));
		return -1;
	}
	
	if ((error = gpgme_data_new_from_cbs (&output, &pkcs7_stream_funcs, ostream)) != GPG_ERR_NO_ERROR) {
		g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open output stream"));
		gpgme_data_release (input);
		return -1;
	}
	
	/* sign the input stream */
	if ((error = gpgme_op_sign (pkcs7->ctx, input, output, GPGME_SIG_MODE_DETACH)) != GPG_ERR_NO_ERROR) {
		g_set_error (err, GMIME_GPGME_ERROR, error, _("Signing failed"));
		gpgme_data_release (output);
		gpgme_data_release (input);
		return -1;
	}
	
	gpgme_data_release (output);
	gpgme_data_release (input);
	
	/* return the digest algorithm used for signing */
	result = gpgme_op_sign_result (pkcs7->ctx);
	
	return pkcs7_digest_id (context, gpgme_hash_algo_name (result->signatures->hash_algo));
#else
	g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("S/MIME support is not enabled in this build"));
	
	return -1;
#endif /* ENABLE_SMIME */
}
Exemple #4
0
gpgme_verify_result_t
gpgme_op_verify_result (gpgme_ctx_t ctx)
{
  void *hook;
  op_data_t opd;
  gpgme_error_t err;
  gpgme_signature_t sig;

  TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx);
  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
  opd = hook;
  if (err || !opd)
    {
      TRACE_SUC0 ("result=(null)");
      return NULL;
    }

  /* It is possible that we saw a new signature only followed by an
     ERROR line for that.  In particular a missing X.509 key triggers
     this.  In this case it is surprising that the summary field has
     not been updated.  We fix it here by explicitly looking for this
     case.  The real fix would be to have GPGME emit ERRSIG.  */
  for (sig = opd->result.signatures; sig; sig = sig->next)
    {
      if (!sig->summary)
        {
          switch (gpg_err_code (sig->status))
            {
            case GPG_ERR_KEY_EXPIRED:
              sig->summary |= GPGME_SIGSUM_KEY_EXPIRED;
              break;

            case GPG_ERR_NO_PUBKEY:
              sig->summary |= GPGME_SIGSUM_KEY_MISSING;
              break;

            default:
              break;
            }
        }
    }

  /* Now for some tracing stuff. */
  if (_gpgme_debug_trace ())
    {
      int i;

      for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++)
	{
	  TRACE_LOG4 ("sig[%i] = fpr %s, summary 0x%x, status %s",
		      i, sig->fpr, sig->summary, gpg_strerror (sig->status));
	  TRACE_LOG6 ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s",
		      i, sig->timestamp, sig->exp_timestamp,
		      sig->wrong_key_usage ? "wrong key usage" : "",
		      sig->pka_trust == 1 ? "pka bad"
		      : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"),
		      sig->chain_model ? "chain model" : "");
	  TRACE_LOG5 ("sig[%i] = validity 0x%x (%s), algos %s/%s",
		      i, sig->validity, gpg_strerror (sig->validity_reason),
		      gpgme_pubkey_algo_name (sig->pubkey_algo),
		      gpgme_hash_algo_name (sig->hash_algo));
	  if (sig->pka_address)
	    {
	      TRACE_LOG2 ("sig[%i] = PKA address %s", i, sig->pka_address);
	    }
	  if (sig->notations)
	    {
	      TRACE_LOG1 ("sig[%i] = has notations (not shown)", i);
	    }
	}
    }

  TRACE_SUC1 ("result=%p", &opd->result);
  return &opd->result;
}
static char * geanypg_result(gpgme_signature_t sig)
{
    char * format =
    _("status ....: %s\n"
      "summary ...:%s\n"
      "fingerprint: %s\n"
      "created ...: %s"
      "expires ...: %s"
      "validity ..: %s\n"
      "val.reason : %s\n"
      "pubkey algo: %s\n"
      "digest algo: %s\n"
      "pka address: %s\n"
      "pka trust .: %s\n"
      "other flags:%s%s\n"
      "notations .: %s\n");
    char * buffer;
    char summary[128];
    const char * pubkey = gpgme_pubkey_algo_name(sig->pubkey_algo);
    const char * hash = gpgme_hash_algo_name(sig->hash_algo);
    char created[64];
    char expires[64];
    size_t buffer_size;
    if (sig->timestamp)
        strncpy(created, ctime((time_t*)&sig->timestamp), 64);
    else
        strcpy(created, _("Unknown\n"));

    if (sig->exp_timestamp)
        strncpy(expires, ctime((time_t*)&sig->exp_timestamp), 64);
    else
        strcpy(expires, _("Unknown\n"));

    buffer_size = strlen(format) +
        strlen(gpgme_strerror(sig->status)) +
        strlen(geanypg_summary(sig->summary, summary)) +
        strlen(sig->fpr ? sig->fpr : _("[None]")) +
        strlen(created) +
        strlen(expires) +
        strlen(geanypg_validity(sig->validity)) +
        strlen(gpgme_strerror(sig->status)) +
        strlen(pubkey ? pubkey : _("Unknown")) +
        strlen(hash ? hash : _("Unknown")) +
        strlen(sig->pka_address ? sig->pka_address : _("[None]")) +
        strlen(sig->pka_trust == 0 ? _("n/a") : sig->pka_trust == 1 ? _("bad") : sig->pka_trust == 2 ? _("okay"): _("RFU")) +
        strlen(sig->wrong_key_usage ? _(" wrong-key-usage") : "") +
        strlen(sig->chain_model ? _(" chain-model") : "") +
        strlen(sig->notations ? _("yes") : _("no")) + 1; // and a trailing \0

    buffer = (char *)calloc(buffer_size, 1);
    memset(summary, 0, 128);
    sprintf(buffer, format,
        gpgme_strerror(sig->status),
        geanypg_summary(sig->summary, summary),
        sig->fpr ? sig->fpr : _("[None]"),
        created,
        expires,
        geanypg_validity(sig->validity),
        gpgme_strerror(sig->status),
        pubkey ? pubkey : _("Unknown"),
        hash ? hash : _("Unknown"),
        sig->pka_address ? sig->pka_address : _("[None]"),
        sig->pka_trust == 0 ? _("n/a") : sig->pka_trust == 1 ? _("bad") : sig->pka_trust == 2 ? _("okay"): _("RFU"),
        sig->wrong_key_usage ? _(" wrong-key-usage") : "", sig->chain_model ? _(" chain-model") : "",
        sig->notations ? _("yes") : _("no"));
    return buffer;
}
const char *GpgME::CreatedSignature::hashAlgorithmAsString() const
{
    return gpgme_hash_algo_name(isNull() ? (gpgme_hash_algo_t)0 : d->created[idx]->hash_algo);
}
Exemple #7
0
gboolean pgpmime_sign(MimeInfo *mimeinfo, PrefsAccount *account, const gchar *from_addr)
{
	MimeInfo *msgcontent, *sigmultipart, *newinfo;
	gchar *textstr, *micalg = NULL;
	FILE *fp;
	gchar *boundary = NULL;
	gchar *sigcontent;
	gpgme_ctx_t ctx;
	gpgme_data_t gpgtext, gpgsig;
	gpgme_error_t err;
	size_t len;
	struct passphrase_cb_info_s info;
	gpgme_sign_result_t result = NULL;
	gchar *test_msg;
	
	fp = my_tmpfile();
	if (fp == NULL) {
		perror("my_tmpfile");
		privacy_set_error(_("Couldn't create temporary file: %s"), g_strerror(errno));
		return FALSE;
	}
	procmime_write_mimeinfo(mimeinfo, fp);
	rewind(fp);

	/* read temporary file into memory */
	test_msg = file_read_stream_to_str(fp);
	claws_fclose(fp);
	
	memset (&info, 0, sizeof info);

	/* remove content node from message */
	msgcontent = (MimeInfo *) mimeinfo->node->children->data;
	g_node_unlink(msgcontent->node);

	/* create temporary multipart for content */
	sigmultipart = procmime_mimeinfo_new();
	sigmultipart->type = MIMETYPE_MULTIPART;
	sigmultipart->subtype = g_strdup("signed");
	
	do {
		g_free(boundary);
		boundary = generate_mime_boundary("Sig");
	} while (strstr(test_msg, boundary) != NULL);
	
	g_free(test_msg);

	g_hash_table_insert(sigmultipart->typeparameters, g_strdup("boundary"),
                            g_strdup(boundary));
	g_hash_table_insert(sigmultipart->typeparameters, g_strdup("protocol"),
                            g_strdup("application/pgp-signature"));
	g_node_append(sigmultipart->node, msgcontent->node);
	g_node_append(mimeinfo->node, sigmultipart->node);

	/* write message content to temporary file */
	fp = my_tmpfile();
	if (fp == NULL) {
		perror("my_tmpfile");
		privacy_set_error(_("Couldn't create temporary file: %s"), g_strerror(errno));
		return FALSE;
	}
	procmime_write_mimeinfo(sigmultipart, fp);
	rewind(fp);

	/* read temporary file into memory */
	textstr = get_canonical_content(fp, boundary);

	g_free(boundary);
	claws_fclose(fp);

	gpgme_data_new_from_mem(&gpgtext, textstr, (size_t)strlen(textstr), 0);
	gpgme_data_new(&gpgsig);
	if ((err = gpgme_new(&ctx)) != GPG_ERR_NO_ERROR) {
		debug_print(("Couldn't initialize GPG context, %s\n"), gpgme_strerror(err));
		privacy_set_error(_("Couldn't initialize GPG context, %s"), gpgme_strerror(err));
		return FALSE;
	}
	gpgme_set_textmode(ctx, 1);
	gpgme_set_armor(ctx, 1);
	gpgme_signers_clear (ctx);

	if (!sgpgme_setup_signers(ctx, account, from_addr)) {
		gpgme_release(ctx);
		return FALSE;
	}

	prefs_gpg_enable_agent(prefs_gpg_get_config()->use_gpg_agent);
	if (g_getenv("GPG_AGENT_INFO") && prefs_gpg_get_config()->use_gpg_agent) {
		debug_print("GPG_AGENT_INFO environment defined, running without passphrase callback\n");
	} else {
   		info.c = ctx;
    		gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
	}

	err = gpgme_op_sign(ctx, gpgtext, gpgsig, GPGME_SIG_MODE_DETACH);
	if (err != GPG_ERR_NO_ERROR) {
		if (err == GPG_ERR_CANCELED) {
			/* ignore cancelled signing */
			privacy_reset_error();
			debug_print("gpgme_op_sign cancelled\n");
		} else {
			privacy_set_error(_("Data signing failed, %s"), gpgme_strerror(err));
			debug_print("gpgme_op_sign error : %x\n", err);
		}
		gpgme_release(ctx);
		return FALSE;
	}
	result = gpgme_op_sign_result(ctx);
	if (result && result->signatures) {
		gpgme_new_signature_t sig = result->signatures;
		if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
			gchar *down_algo = g_ascii_strdown(gpgme_hash_algo_name(
				result->signatures->hash_algo), -1);
			micalg = g_strdup_printf("pgp-%s", down_algo);
			g_free(down_algo);
		} else {
			micalg = g_strdup(gpgme_hash_algo_name(
				result->signatures->hash_algo));
		}
		while (sig) {
			debug_print("valid signature: %s\n", sig->fpr);
			sig = sig->next;
		}
	} else if (result && result->invalid_signers) {
		gpgme_invalid_key_t invalid = result->invalid_signers;
		while (invalid) {
			g_warning("invalid signer: %s (%s)", invalid->fpr, 
				gpgme_strerror(invalid->reason));
			privacy_set_error(_("Data signing failed due to invalid signer: %s"), 
				gpgme_strerror(invalid->reason));
			invalid = invalid->next;
		}
		gpgme_release(ctx);
		return FALSE;
	} else {
		/* can't get result (maybe no signing key?) */
		debug_print("gpgme_op_sign_result error\n");
		privacy_set_error(_("Data signing failed, no results."));
		gpgme_release(ctx);
		return FALSE;
	}

	sigcontent = sgpgme_data_release_and_get_mem(gpgsig, &len);
	gpgme_data_release(gpgtext);
	g_free(textstr);

	if (sigcontent == NULL || len <= 0) {
		g_warning("sgpgme_data_release_and_get_mem failed");
		privacy_set_error(_("Data signing failed, no contents."));
		g_free(micalg);
		g_free(sigcontent);
		return FALSE;
	}

	/* add signature */
	g_hash_table_insert(sigmultipart->typeparameters, g_strdup("micalg"),
                            micalg);

	newinfo = procmime_mimeinfo_new();
	newinfo->type = MIMETYPE_APPLICATION;
	newinfo->subtype = g_strdup("pgp-signature");
	newinfo->description = g_strdup(_("OpenPGP digital signature"));
	newinfo->content = MIMECONTENT_MEM;
	newinfo->data.mem = g_malloc(len + 1);
	g_memmove(newinfo->data.mem, sigcontent, len);
	newinfo->data.mem[len] = '\0';
	g_node_append(sigmultipart->node, newinfo->node);

	g_free(sigcontent);
	gpgme_release(ctx);

	return TRUE;
}
Exemple #8
0
gboolean
g_mime_gpgme_mps_sign(GMimeMultipartSigned * mps, GMimeObject * content,
		      const gchar * userid, gpgme_protocol_t protocol,
		      GtkWindow * parent, GError ** err)
{
    GMimeStream *stream;
    GMimeStream *filtered;
    GMimeStream *sigstream;
    GMimeFilter *filter;
    GMimeContentType *content_type;
    GMimeDataWrapper *wrapper;
    GMimeParser *parser;
    GMimePart *signature;
    gchar *micalg;
    const gchar *proto_type;
    const gchar *sig_subtype;
    gpgme_hash_algo_t hash_algo;

    g_return_val_if_fail(GMIME_IS_MULTIPART_SIGNED(mps), FALSE);
    g_return_val_if_fail(GMIME_IS_OBJECT(content), FALSE);

    /* Prepare all the parts for signing... */
    sign_prepare(content);

    /* get the cleartext */
    stream = g_mime_stream_mem_new();
    filtered = g_mime_stream_filter_new(stream);

    /* Note: see rfc3156, section 3 - second note */
    filter = g_mime_filter_from_new(GMIME_FILTER_FROM_MODE_ARMOR);
    g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
    g_object_unref(filter);

    /* Note: see rfc3156, section 5.4 (this is the main difference between rfc2015 and rfc3156) */
    filter = g_mime_filter_strip_new();
    g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
    g_object_unref(filter);

    g_mime_object_write_to_stream(content, filtered);
    g_mime_stream_flush(filtered);
    g_object_unref(filtered);
    g_mime_stream_reset(stream);

    /* Note: see rfc2015 or rfc3156, section 5.1 */
    filtered = g_mime_stream_filter_new(stream);
    filter = g_mime_filter_crlf_new(TRUE, FALSE);
    g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
    g_object_unref(filter);

    /* construct the signature stream */
    sigstream = g_mime_stream_mem_new();

    /* sign the content stream */
    hash_algo =
	libbalsa_gpgme_sign(userid, filtered, sigstream, protocol, FALSE,
			    parent, err);
    g_object_unref(filtered);
    if (hash_algo == GPGME_MD_NONE) {
	g_object_unref(sigstream);
	g_object_unref(stream);
	return FALSE;
    }

    g_mime_stream_reset(sigstream);
    g_mime_stream_reset(stream);

    /* set the multipart/signed protocol and micalg */
    content_type = g_mime_object_get_content_type(GMIME_OBJECT(mps));
    if (protocol == GPGME_PROTOCOL_OpenPGP) {
	micalg =
	    g_strdup_printf("PGP-%s", gpgme_hash_algo_name(hash_algo));
	proto_type = "application/pgp-signature";
	sig_subtype = "pgp-signature";
    } else {
	micalg = g_strdup(gpgme_hash_algo_name(hash_algo));
	proto_type = "application/pkcs7-signature";
	sig_subtype = "pkcs7-signature";
    }
    g_mime_content_type_set_parameter(content_type, "micalg", micalg);
    g_free(micalg);
    g_mime_content_type_set_parameter(content_type, "protocol", proto_type);
    g_mime_multipart_set_boundary(GMIME_MULTIPART(mps), NULL);

    /* construct the content part */
    parser = g_mime_parser_new_with_stream(stream);
    content = g_mime_parser_construct_part(parser);
    g_object_unref(stream);
    g_object_unref(parser);

    /* construct the signature part */
    signature = g_mime_part_new_with_type("application", sig_subtype);

    wrapper = g_mime_data_wrapper_new();
    g_mime_data_wrapper_set_stream(wrapper, sigstream);
    g_mime_part_set_content_object(signature, wrapper);
    g_object_unref(sigstream);
    g_object_unref(wrapper);

    /* FIXME: temporary hack, this info should probably be set in
     * the CipherContext class - maybe ::sign can take/output a
     * GMimePart instead. */
    if (protocol == GPGME_PROTOCOL_CMS) {
	g_mime_part_set_content_encoding(signature,
					 GMIME_CONTENT_ENCODING_BASE64);
	g_mime_part_set_filename(signature, "smime.p7m");
    }

    /* save the content and signature parts */
    /* FIXME: make sure there aren't any other parts?? */
    g_mime_multipart_add(GMIME_MULTIPART(mps), content);
    g_mime_multipart_add(GMIME_MULTIPART(mps), (GMimeObject *) signature);
    g_object_unref(signature);
    g_object_unref(content);

    return TRUE;
}
Exemple #9
0
static void
print_result (gpgme_verify_result_t result)
{
    gpgme_signature_t sig;
    gpgme_sig_notation_t nt;
    gpgme_tofu_info_t ti;
    int count = 0;

    printf ("Original file name: %s\n", nonnull(result->file_name));
    for (sig = result->signatures; sig; sig = sig->next)
    {
        printf ("Signature %d\n", count++);
        printf ("  status ....: %s\n", gpgme_strerror (sig->status));
        printf ("  summary ...:");
        print_summary (sig->summary);
        putchar ('\n');
        printf ("  fingerprint: %s\n", nonnull (sig->fpr));
        printf ("  created ...: %lu\n", sig->timestamp);
        printf ("  expires ...: %lu\n", sig->exp_timestamp);
        printf ("  validity ..: ");
        print_validity (sig->validity);
        putchar ('\n');
        printf ("  val.reason : %s\n", gpgme_strerror (sig->status));
        printf ("  pubkey algo: %d (%s)\n", sig->pubkey_algo,
                nonnull(gpgme_pubkey_algo_name (sig->pubkey_algo)));
        printf ("  digest algo: %d (%s)\n", sig->hash_algo,
                nonnull(gpgme_hash_algo_name (sig->hash_algo)));
        printf ("  pka address: %s\n", nonnull (sig->pka_address));
        printf ("  pka trust .: %s\n",
                sig->pka_trust == 0? "n/a" :
                sig->pka_trust == 1? "bad" :
                sig->pka_trust == 2? "okay": "RFU");
        printf ("  other flags:%s%s\n",
                sig->wrong_key_usage? " wrong-key-usage":"",
                sig->chain_model? " chain-model":""
               );
        for (nt = sig->notations; nt; nt = nt->next)
        {
            printf ("  notation ..: '%s'\n", nt->name);
            if (strlen (nt->name) != nt->name_len)
                printf ("    warning : name larger (%d)\n", nt->name_len);
            printf ("    flags ...:%s%s (0x%02x)\n",
                    nt->critical? " critical":"",
                    nt->human_readable? " human":"",
                    nt->flags);
            if (nt->value)
                printf ("    value ...: '%s'\n", nt->value);
            if ((nt->value?strlen (nt->value):0) != nt->value_len)
                printf ("    warning : value larger (%d)\n", nt->value_len);
        }
        for (ti = sig->tofu; ti; ti = ti->next)
        {
            printf ("  tofu addr .: %s\n", ti->address);
            if (!sig->fpr || strcmp (sig->fpr, ti->fpr))
                printf ("    WARNING .: fpr mismatch (%s)\n", ti->fpr);
            printf ("    validity : %u (%s)\n", ti->validity,
                    ti->validity == 0? "conflict" :
                    ti->validity == 1? "no history" :
                    ti->validity == 2? "little history" :
                    ti->validity == 3? "enough history" :
                    ti->validity == 4? "lot of history" : "?");
            printf ("    policy ..: %u (%s)\n", ti->policy,
                    ti->policy == GPGME_TOFU_POLICY_NONE? "none" :
                    ti->policy == GPGME_TOFU_POLICY_AUTO? "auto" :
                    ti->policy == GPGME_TOFU_POLICY_GOOD? "good" :
                    ti->policy == GPGME_TOFU_POLICY_UNKNOWN? "unknown" :
                    ti->policy == GPGME_TOFU_POLICY_BAD? "bad" :
                    ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?");
            printf ("    sigcount : %hu\n", ti->signcount);
            printf ("    firstseen: %u\n", ti->firstseen);
            printf ("    lastseen : %u\n", ti->lastseen);
            printf ("    desc ....: ");
            print_description (nonnull (ti->description), 15);
        }
    }
}