static const guchar*
find_extension (GNode *asn, const guchar *data, gsize n_data, const gchar *oid, gsize *n_extension)
{
	const guchar *value;
	GNode *node = NULL;
	gchar *exoid;
	guint index;
	int len;

	len = strlen (oid);

	for (index = 1; TRUE; ++index) {

		/* Make sure it is present */
		node = egg_asn1x_node (asn, "tbsCertificate", "extensions", index, "extnID", NULL);
		if (node == NULL)
			return NULL;

		exoid = egg_asn1x_get_oid_as_string (node);
		g_assert (exoid);

		if (strcmp (exoid, oid) == 0) {
			g_free (exoid);
			node = egg_asn1x_node (asn, "tbsCertificate", "extensions", index, "extnValue", NULL);
			value = egg_asn1x_get_raw_value (node, n_extension);
			g_assert (value);
			return value;
		}

		g_free (exoid);
	}

	g_assert_not_reached ();
}
static gboolean
append_extension (GcrCertificateDetailsWidget *self, GNode *asn,
                  const guchar *data, gsize n_data, gint index)
{
	GQuark oid;
	gchar *display;
	gsize n_value;
	const guchar *value;
	const gchar *text;
	gboolean critical;
	GNode *node;

	/* Make sure it is present */
	asn = egg_asn1x_node (asn, "tbsCertificate", "extensions", index, NULL);
	if (asn == NULL)
		return FALSE;

	/* Dig out the OID */
	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "extnID", NULL));
	g_return_val_if_fail (oid, FALSE);


	append_heading (self, _("Extension"));


	/* Extension type */
	text = egg_oid_get_description (oid);
	append_field_and_value (self, _("Identifier"), text, FALSE);


	/* Extension value */
	value = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "extnValue", NULL), &n_value);

	/* TODO: Parsing of extensions that we understand */
	display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
	append_field_and_value (self, _("Value"), display, TRUE);
	g_free (display);


	/* Critical */
	node = egg_asn1x_node (asn, "critical", NULL);
	if (node != NULL) {
		if (egg_asn1x_get_boolean (node, &critical))
			append_field_and_value (self, _("Critical"), critical ? _("Yes") : _("No"), FALSE);
	}

	return TRUE;
}
static guint
calculate_dsa_params_size (gconstpointer data, gsize n_data)
{
	GNode *asn;
	gsize n_content;

	asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", data, n_data);
	g_return_val_if_fail (asn, 0);

	if (!egg_asn1x_get_raw_value (egg_asn1x_node (asn, "p", NULL), &n_content))
		g_return_val_if_reached (0);

	egg_asn1x_destroy (asn);

	/* Removes the complement */
	return (n_content / 2) * 2 * 8;
}
gboolean
gkm_data_asn1_read_mpi (GNode *asn, gcry_mpi_t *mpi)
{
	gcry_error_t gcry;
	gsize sz;
	const guchar *buf;

	g_return_val_if_fail (asn, FALSE);
	g_return_val_if_fail (mpi, FALSE);

	buf = egg_asn1x_get_raw_value (asn, &sz);
	if (!buf)
		return FALSE;

	/* Automatically stores in secure memory if DER data is secure */
	gcry = gcry_mpi_scan (mpi, GCRYMPI_FMT_STD, buf, sz, &sz);
	if (gcry != 0)
		return FALSE;

	return TRUE;
}
const guchar*
gkm_certificate_get_extension (GkmCertificate *self, GQuark oid,
                               gsize *n_extension, gboolean *critical)
{
	guchar *val;
	gsize n_val;
	gint index;

	g_return_val_if_fail (GKM_IS_CERTIFICATE (self), NULL);
	g_return_val_if_fail (self->pv->asn1, NULL);
	g_return_val_if_fail (oid, NULL);
	g_return_val_if_fail (n_extension, NULL);

	index = find_certificate_extension (self, oid);
	if (index <= 0)
		return NULL;

	/* Read the critical status */
	if (critical) {
		val = egg_asn1x_get_string_as_raw (egg_asn1x_node (self->pv->asn1, "tbsCertificate",
		                                   "extensions", index, "critical", NULL), NULL, &n_val);

		/*
		 * We're pretty liberal in what we accept as critical. The goal
		 * here is not to accidentally mark as non-critical what some
		 * other x509 implementation meant to say critical.
		 */
		if (!val || n_val < 1 || g_ascii_toupper (val[0]) != 'T')
			*critical = FALSE;
		else
			*critical = TRUE;
		g_free (val);
	}

	/* And the extension value */
	return egg_asn1x_get_raw_value (egg_asn1x_node (self->pv->asn1, "tbsCertificate",
	                                "extensions", index, "extnValue", NULL), n_extension);
}
static void
refresh_display (GcrCertificateDetailsWidget *self)
{
	GtkTextIter start, iter;
	const guchar *data, *value;
	gsize n_data, n_value;
	const gchar *text;
	gulong version;
	guint index, size, n_bits;
	gchar *display;
	guchar *bits;
	GNode *asn;
	GQuark oid;
	GDate date;

	gtk_text_buffer_get_start_iter (self->pv->buffer, &start);
	gtk_text_buffer_get_end_iter (self->pv->buffer, &iter);
	gtk_text_buffer_delete (self->pv->buffer, &start, &iter);
	
	if (!self->pv->certificate)
		return;
	
	data = gcr_certificate_get_der_data (self->pv->certificate, &n_data);
	g_return_if_fail (data);

	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", data, n_data);
	g_return_if_fail (asn);

	/* The subject */
	append_heading (self, _("Subject Name"));
	egg_dn_parse (egg_asn1x_node (asn, "tbsCertificate", "subject", "rdnSequence", NULL), on_parsed_dn_part, self);

	/* The Issuer */
	append_heading (self, _("Issuer Name"));
	egg_dn_parse (egg_asn1x_node (asn, "tbsCertificate", "issuer", "rdnSequence", NULL), on_parsed_dn_part, self);

	/* The Issued Parameters */
	append_heading (self, _("Issued Certificate"));

	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "tbsCertificate", "version", NULL), &version))
		g_return_if_reached ();
	display = g_strdup_printf ("%lu", version + 1);
	append_field_and_value (self, _("Version"), display, FALSE);
	g_free (display);

	value = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "tbsCertificate", "serialNumber", NULL), &n_value);
	g_return_if_fail (value);
	display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
	append_field_and_value (self, _("Serial Number"), display, TRUE);
	g_free (display);
	
	display = g_malloc0 (128);
	if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notBefore", NULL), &date)) {
		if (!g_date_strftime (display, 128, "%Y-%m-%d", &date))
			g_return_if_reached ();
		append_field_and_value (self, _("Not Valid Before"), display, FALSE);
	}
	if (egg_asn1x_get_time_as_date (egg_asn1x_node (asn, "tbsCertificate", "validity", "notAfter", NULL), &date)) {
		if (!g_date_strftime (display, 128, "%Y-%m-%d", &date))
			g_return_if_reached ();
		append_field_and_value (self, _("Not Valid After"), display, FALSE);
	}
	g_free (display);
	
	/* Signature */
	append_heading (self, _("Signature"));

	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "signatureAlgorithm", "algorithm", NULL));
	text = egg_oid_get_description (oid);
	append_field_and_value (self, _("Signature Algorithm"), text, FALSE);

	value = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "signatureAlgorithm", "parameters", NULL), &n_value);
	if (value && n_value) {
		display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
		append_field_and_value (self, _("Signature Parameters"), display, TRUE);
		g_free (display);
	}
	
	value = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "signature", NULL), &n_value);
	g_return_if_fail (value);
	display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
	append_field_and_value (self, _("Signature"), display, TRUE);
	g_free (display);

	/* Public Key Info */
	append_heading (self, _("Public Key Info"));

	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "tbsCertificate", "subjectPublicKeyInfo", "algorithm", "algorithm", NULL));
	text = egg_oid_get_description (oid);
	append_field_and_value (self, _("Key Algorithm"), text, FALSE);

	value = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "tbsCertificate", "subjectPublicKeyInfo", "algorithm", "parameters", NULL), &n_value);
	if (value && n_value) {
		display = egg_hex_encode_full (value, n_value, TRUE, ' ', 1);
		append_field_and_value (self, _("Key Parameters"), display, TRUE);
		g_free (display);
	}

	size = gcr_certificate_get_key_size (self->pv->certificate);
	if (size > 0) {
		display = g_strdup_printf ("%u", size); 
		append_field_and_value (self, _("Key Size"), display, FALSE);
		g_free (display);
	}

	bits = egg_asn1x_get_bits_as_raw (egg_asn1x_node (asn, "tbsCertificate", "subjectPublicKeyInfo", "subjectPublicKey", NULL), NULL, &n_bits);
	g_return_if_fail (bits);
	display = egg_hex_encode_full (bits, n_bits / 8, TRUE, ' ', 1);
	append_field_and_value (self, _("Public Key"), display, TRUE);
	g_free (display);
	g_free (bits);

	/* Fingerprints */
	append_heading (self, _("Fingerprints"));
	
	append_fingerprint (self, data, n_data, "SHA1", G_CHECKSUM_SHA1);
	append_fingerprint (self, data, n_data, "MD5", G_CHECKSUM_MD5);
	
	/* Extensions */
	for (index = 1; TRUE; ++index) {
		if (!append_extension (self, asn, data, n_data, index))
			break;
	}

	egg_asn1x_destroy (asn);
}
GkmDataResult
gkm_data_der_read_private_pkcs8_plain (const guchar *data, gsize n_data, gcry_sexp_t *s_key)
{
	GNode *asn = NULL;
	GkmDataResult ret;
	int algorithm;
	GQuark key_algo;
	const guchar *keydata;
	gsize n_keydata;
	const guchar *params;
	gsize n_params;

	ret = GKM_DATA_UNRECOGNIZED;

	init_quarks ();

	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-8-PrivateKeyInfo", data, n_data);
	if (!asn)
		goto done;

	ret = GKM_DATA_FAILURE;
	algorithm = 0;

	key_algo = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "privateKeyAlgorithm", "algorithm", NULL));
	if (!key_algo)
		goto done;
	else if (key_algo == OID_PKIX1_RSA)
		algorithm = GCRY_PK_RSA;
	else if (key_algo == OID_PKIX1_DSA)
		algorithm = GCRY_PK_DSA;

	if (!algorithm) {
		ret = GKM_DATA_UNRECOGNIZED;
		goto done;
	}

	keydata = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "privateKey", NULL), &n_keydata);
	if (!keydata)
		goto done;

	params = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "privateKeyAlgorithm", "parameters", NULL),
	                                    &n_params);

	ret = GKM_DATA_SUCCESS;

done:
	if (ret == GKM_DATA_SUCCESS) {
		switch (algorithm) {
		case GCRY_PK_RSA:
			ret = gkm_data_der_read_private_key_rsa (keydata, n_keydata, s_key);
			break;
		case GCRY_PK_DSA:
			/* Try the normal one block format */
			ret = gkm_data_der_read_private_key_dsa (keydata, n_keydata, s_key);

			/* Otherwise try the two part format that everyone seems to like */
			if (ret == GKM_DATA_UNRECOGNIZED && params && n_params)
				ret = gkm_data_der_read_private_key_dsa_parts (keydata, n_keydata,
				                                               params, n_params, s_key);
			break;
		default:
			g_message ("invalid or unsupported key type in PKCS#8 key");
			ret = GKM_DATA_UNRECOGNIZED;
			break;
		};

	} else if (ret == GKM_DATA_FAILURE) {
		g_message ("invalid PKCS#8 key");
	}

	egg_asn1x_destroy (asn);
	return ret;
}