Noteworthy changes in version 1.x.y (unreleased)
------------------------------------------------
- * bla bla bla ...
+ * Reading signature notations and policy URLs on key signatures is
+ supported. They can be found in the new field notations of the
+ gpgme_key_sig_t structure. This has to be enabled with the keylist
+ mode flag GPGME_KEYLIST_MODE_SIG_NOTATIONS.
* Interface changes relative to the 1.0.3 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-something changed
+gpgme_key_sig_t EXTENDED: New field notations.
+GPGME_KEYLIST_MODE_SIG_NOTATIONS NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+2005-10-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.texi (Key Management): Add the new member notations of
+ gpgme_sig_key_t.
+ (Key Listing Mode): Document GPGME_KEYLIST_MODE_SIG_NOTATIONS.
+
2005-10-01 Marcus Brinkmann <marcus@g10code.de>
* gpgme.texi: Enclose all return parameters of deftypefuns in
The @code{GPGME_KEYLIST_MODE_SIGS} symbol specifies that the key
signatures should be included in the listed keys.
+@item GPGME_KEYLIST_MODE_SIG_NOTATIONS
+The @code{GPGME_KEYLIST_MODE_SIG_NOTATIONS} symbol specifies that the
+signature notations on key signatures should be included in the listed
+keys. This only works if @code{GPGME_KEYLIST_MODE_SIGS} is also
+enabled.
+
@item GPGME_KEYLIST_MODE_VALIDATE
The @code{GPGME_KEYLIST_MODE_VALIDATE} symbol specifies that the
backend should do key or certificate validation and not just get the
The signatures on a key are only available if the key was retrieved
via a listing operation with the @code{GPGME_KEYLIST_MODE_SIGS} mode
-enabled, because it is expensive to retrieve all signatures of a key.
+enabled, because it can be expensive to retrieve all signatures of a
+key.
+
+The signature notations on a key signature are only available if the
+key was retrieved via a listing operation with the
+@code{GPGME_KEYLIST_MODE_SIG_NOTATIONS} mode enabled, because it can
+be expensive to retrieve all signature notations.
The key signature structure has the following members:
@item char *email
This is the email component of @code{uid}, if available.
+
+@item gpgme_sig_notation_t notations
+This is a linked list with the notation data and policy URLs.
@end table
@end deftp
+2005-10-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * util.h (_gpgme_decode_percent_string): Add new argument BINARY
+ to prototype.
+ * verify.c (parse_notation): Likewise for invocation.
+ * conversion.c (_gpgme_decode_percent_string): Likewise to
+ declaration. If set, do not replace '\0' characters with a
+ printable string.
+ * gpgme.h (struct _gpgme_key_sig): New field notations.
+ * ops.h (_gpgme_parse_notation): New prototype.
+ * sig-notation.c (_gpgme_parse_notation): New function.
+ * key.c (gpgme_key_unref): Free all signature notations.
+ * keylist.c (op_data_t): New member tmp_keysig.
+ (finish_key): Clear OPD->tmp_keysig.
+ * gpgme.c (gpgme_set_keylist_mode): Remove check.
+ * rungpg.c (gpg_keylist): Support listing signature notations.
+ (gpg_keylist_ext): Likewise.
+
2005-10-01 Marcus Brinkmann <marcus@g10code.de>
* engine.h (_gpgme_set_engine_info): Add prototype.
large enough buffer is allocated with malloc and *DESTP is set to
the result. Currently, LEN is only used to specify if allocation
is desired or not, the caller is expected to make sure that *DESTP
- is large enough if LEN is not zero. */
+ is large enough if LEN is not zero. If BINARY is 1, then '\0'
+ characters are allowed in the output. */
gpgme_error_t
-_gpgme_decode_percent_string (const char *src, char **destp, size_t len)
+_gpgme_decode_percent_string (const char *src, char **destp, size_t len,
+ int binary)
{
char *dest;
}
else
{
- if (!val)
+ if (!val && !binary)
{
/* A binary zero is not representable in a C
string. */
gpgme_error_t
gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode)
{
- if (!((mode & GPGME_KEYLIST_MODE_LOCAL)
- || (mode & GPGME_KEYLIST_MODE_EXTERN)
- || (mode & GPGME_KEYLIST_MODE_SIGS)))
- return gpg_error (GPG_ERR_INV_VALUE);
-
ctx->keylist_mode = mode;
return 0;
}
\f
/* The available keylist mode flags. */
-#define GPGME_KEYLIST_MODE_LOCAL 1
-#define GPGME_KEYLIST_MODE_EXTERN 2
-#define GPGME_KEYLIST_MODE_SIGS 4
-#define GPGME_KEYLIST_MODE_VALIDATE 256
+#define GPGME_KEYLIST_MODE_LOCAL 1
+#define GPGME_KEYLIST_MODE_EXTERN 2
+#define GPGME_KEYLIST_MODE_SIGS 4
+#define GPGME_KEYLIST_MODE_SIG_NOTATIONS 8
+#define GPGME_KEYLIST_MODE_VALIDATE 256
typedef unsigned int gpgme_keylist_mode_t;
/* Crypto backend specific signature class. */
unsigned int sig_class;
+
+ /* Notation data and policy URLs. */
+ gpgme_sig_notation_t notations;
+
+ /* Internal to GPGME, do not use. */
+ gpgme_sig_notation_t _last_notation;
};
typedef struct _gpgme_key_sig *gpgme_key_sig_t;
while (keysig)
{
- gpgme_key_sig_t next = keysig->next;
+ gpgme_key_sig_t next_keysig = keysig->next;
+ gpgme_sig_notation_t notation = keysig->notations;
+
+ while (notation)
+ {
+ gpgme_sig_notation_t next_notation = notation->next;
+
+ _gpgme_sig_notation_free (notation);
+ notation = next_notation;
+ }
+
free (keysig);
- keysig = next;
+ keysig = next_keysig;
}
free (uid);
uid = next_uid;
struct _gpgme_op_keylist_result result;
gpgme_key_t tmp_key;
+
/* This points to the last uid in tmp_key. */
gpgme_user_id_t tmp_uid;
+
+ /* This points to the last sig in tmp_uid. */
+ gpgme_key_sig_t tmp_keysig;
+
/* Something new is available. */
int key_cond;
struct key_queue_item_s *key_queue;
if (opd->tmp_key)
gpgme_key_unref (opd->tmp_key);
- /* opd->tmp_uid is actually part of opd->tmp_key, so we do not need
- to release it here. */
+
+ /* opd->tmp_uid and opd->tmp_keysig are actually part of opd->tmp_key,
+ so we do not need to release them here. */
while (key)
{
opd->tmp_key = NULL;
opd->tmp_uid = NULL;
+ opd->tmp_keysig = NULL;
if (key)
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
enum
{
RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR,
- RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV
+ RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK
}
rectype = RT_NONE;
#define NR_FIELDS 13
rectype = RT_SUB;
else if (!strcmp (field[0], "ssb") && key)
rectype = RT_SSB;
+ else if (!strcmp (field[0], "spk") && key)
+ rectype = RT_SPK;
else
rectype = RT_NONE;
if (rectype != RT_SIG && rectype != RT_REV)
opd->tmp_uid = NULL;
+ /* Only look at subpackets immediately following a signature. For
+ this, clear the signature pointer when encountering anything but
+ a subpacket. */
+ if (rectype != RT_SPK)
+ opd->tmp_keysig = NULL;
+
switch (rectype)
{
case RT_PUB:
if (field[10][2] == 'x')
keysig->exportable = 1;
}
+
+ opd->tmp_keysig = keysig;
break;
+ case RT_SPK:
+ if (!opd->tmp_keysig)
+ return 0;
+ assert (opd->tmp_keysig == key->_last_uid->_last_keysig);
+
+ if (fields >= 4)
+ {
+ /* Field 2 has the subpacket type. */
+ int type = atoi (field[1]);
+
+ /* Field 3 has the flags. */
+ int flags = atoi (field[2]);
+
+ /* Field 4 has the length. */
+ int len = atoi (field[3]);
+
+ /* Field 5 has the data. */
+ char *data = field[4];
+
+ /* Type 20: Notation data. */
+ /* Type 26: Policy URL. */
+ if (type == 20 || type == 26)
+ {
+ gpgme_sig_notation_t notation;
+
+ keysig = opd->tmp_keysig;
+
+ /* At this time, any error is serious. */
+ err = _gpgme_parse_notation (¬ation, type, flags, len, data);
+ if (err)
+ return err;
+
+ /* Add a new notation. FIXME: Could be factored out. */
+ if (!keysig->notations)
+ keysig->notations = notation;
+ if (keysig->_last_notation)
+ keysig->_last_notation->next = notation;
+ keysig->_last_notation = notation;
+ }
+ }
+
case RT_NONE:
/* Unknown record. */
break;
pointer is ignored. */
void _gpgme_sig_notation_free (gpgme_sig_notation_t notation);
+/* Parse a notation or policy URL subpacket. If the packet type is
+ not known, return no error but NULL in NOTATION. */
+gpgme_error_t _gpgme_parse_notation (gpgme_sig_notation_t *notationp,
+ int type, int pkflags, int len,
+ char *data);
+
#endif /* OPS_H */
err = add_arg (gpg, "--with-fingerprint");
if (!err)
err = add_arg (gpg, "--with-fingerprint");
+ if (!err && (mode & GPGME_KEYLIST_MODE_SIGS)
+ && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
+ {
+ err = add_arg (gpg, "--list-options");
+ if (!err)
+ err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
+ }
if (!err)
err = add_arg (gpg, secret_only ? "--list-secret-keys"
: ((mode & GPGME_KEYLIST_MODE_SIGS)
err = add_arg (gpg, "--with-fingerprint");
if (!err)
err = add_arg (gpg, "--with-fingerprint");
+ if (!err && (mode & GPGME_KEYLIST_MODE_SIGS)
+ && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
+ {
+ err = add_arg (gpg, "--list-options");
+ if (!err)
+ err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
+ }
if (!err)
err = add_arg (gpg, secret_only ? "--list-secret-keys"
: ((mode & GPGME_KEYLIST_MODE_SIGS)
if (!notation)
return gpg_error_from_errno (errno);
- if (name_len)
+ /* This is critical. We want to reliably identify policy URLs by
+ using a NULL pointer for NAME. So all notations must have a NAME
+ string, even if it is empty. */
+ if (name)
{
/* We add a trailing '\0' for stringification in the good
case. */
notation->name_len = name_len;
}
- if (value_len)
+ if (value)
{
/* We add a trailing '\0' for stringification in the good
case. */
_gpgme_sig_notation_free (notation);
return err;
}
+
+\f
+/* GnuPG subpacket flags. */
+
+/* This subpacket data is part of the hashed data. */
+#define GNUPG_SPK_HASHED 0x01
+
+/* This subpacket is marked critical. */
+#define GNUPG_SPK_CRITICAL 0x02
+
+/* Parse a notation or policy URL subpacket. If the packet type is
+ not known, return no error but NULL in NOTATION. */
+gpgme_error_t
+_gpgme_parse_notation (gpgme_sig_notation_t *notationp,
+ int type, int pkflags, int len, char *data)
+{
+ gpgme_error_t err;
+ char *name = NULL;
+ int name_len = 0;
+ char *value = NULL;
+ int value_len = 0;
+ gpgme_sig_notation_flags_t flags = 0;
+ char *decoded_data;
+ unsigned char *bdata;
+
+ /* Type 20: Notation data. */
+ /* Type 26: Policy URL. */
+ if (type != 20 && type != 26)
+ {
+ *notationp = NULL;
+ return 0;
+ }
+
+ /* A few simple sanity checks. */
+ if (len > strlen (data))
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ /* See below for the format of a notation subpacket. It has at
+ least four octets of flags and two times two octets of length
+ information. */
+ if (type == 20 && len < 4 + 2 + 2)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ err = _gpgme_decode_percent_string (data, &decoded_data, 0, 1);
+ if (err)
+ return err;
+ bdata = (unsigned char *) decoded_data;
+
+ /* Flags common to notation data and policy URL. */
+ if (pkflags & GNUPG_SPK_CRITICAL)
+ flags |= GPGME_SIG_NOTATION_CRITICAL;
+
+ /* This information is relevant in parsing multi-octet numbers below:
+
+ 3.1. Scalar numbers
+
+ Scalar numbers are unsigned, and are always stored in big-endian
+ format. Using n[k] to refer to the kth octet being interpreted,
+ the value of a two-octet scalar is ((n[0] << 8) + n[1]). The
+ value of a four-octet scalar is ((n[0] << 24) + (n[1] << 16) +
+ (n[2] << 8) + n[3]).
+
+ From RFC2440: OpenPGP Message Format. Copyright (C) The Internet
+ Society (1998). All Rights Reserved. */
+#define RFC2440_GET_WORD(chr) ((((int)((unsigned char *)(chr))[0]) << 8) \
+ + ((int)((unsigned char *)(chr))[1]))
+
+ if (type == 20)
+ {
+ /* 5.2.3.15. Notation Data
+
+ (4 octets of flags, 2 octets of name length (M),
+ 2 octets of value length (N), M octets of name data,
+ N octets of value data)
+
+ [...] The "flags" field holds four octets of flags.
+ All undefined flags MUST be zero. Defined flags are:
+
+ First octet: 0x80 = human-readable. [...]
+ Other octets: none.
+
+ From RFC2440: OpenPGP Message Format. Copyright (C) The
+ Internet Society (1998). All Rights Reserved. */
+
+ int chr;
+
+ /* First octet of flags. */
+#define RFC2440_SPK20_FLAG1_HUMAN_READABLE 0x80
+
+ chr = *bdata;
+ bdata++;
+
+ if (chr & RFC2440_SPK20_FLAG1_HUMAN_READABLE)
+ flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;
+
+ /* The second, third and four octet of flags are unused. */
+ bdata++;
+ bdata++;
+ bdata++;
+
+ name_len = RFC2440_GET_WORD (bdata);
+ bdata += 2;
+
+ value_len = RFC2440_GET_WORD (bdata);
+ bdata += 2;
+
+ /* Small sanity check. */
+ if (4 + 2 + 2 + name_len + value_len > len)
+ {
+ free (decoded_data);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+
+ name = (char *) bdata;
+ bdata += name_len;
+
+ value = (char *) bdata;
+ }
+ else
+ {
+ /* Type is 26. */
+
+ /* NAME is NULL, name_len is 0. */
+
+ value = (char *) bdata;
+ value_len = strlen (value);
+ }
+
+ err = _gpgme_sig_notation_create (notationp, name, name_len,
+ value, value_len, flags);
+
+ free (decoded_data);
+ return err;
+}
large enough buffer is allocated with malloc and *DESTP is set to
the result. Currently, LEN is only used to specify if allocation
is desired or not, the caller is expected to make sure that *DESTP
- is large enough if LEN is not zero. */
+ is large enough if LEN is not zero. If BINARY is 1, then '\0'
+ characters are allowed in the output. */
gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp,
- size_t len);
+ size_t len, int binary);
/* Parse the string TIMESTAMP into a time_t. The string may either be
if (code == GPGME_STATUS_NOTATION_NAME)
{
- err = _gpgme_decode_percent_string (args, ¬ation->name, 0);
+ err = _gpgme_decode_percent_string (args, ¬ation->name, 0, 0);
if (err)
{
_gpgme_sig_notation_free (notation);
{
/* This is a policy URL. */
- err = _gpgme_decode_percent_string (args, ¬ation->value, 0);
+ err = _gpgme_decode_percent_string (args, ¬ation->value, 0, 0);
if (err)
{
_gpgme_sig_notation_free (notation);
dest += cur_len;
}
- err = _gpgme_decode_percent_string (args, &dest, len);
+ err = _gpgme_decode_percent_string (args, &dest, len, 0);
if (err)
return err;