summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2026-05-17 12:59:42 +0200
committerWerner Koch <[email protected]>2026-05-17 12:59:42 +0200
commit8081d34e5f620a614f58617700f9b473392e6478 (patch)
tree6322bb0a23a303ccc71e6c313856ab5dcca654ca
parentengine:gpgconf: Increase the buffer size. (diff)
downloadgpgme-8081d34e5f620a614f58617700f9b473392e6478.tar.gz
gpgme-8081d34e5f620a614f58617700f9b473392e6478.zip
Support setting attributes for CMS signatures.
* src/gpgme.h.in (GPGME_SIG_NOTATION_UNPROTECTED): New. (struct _gpgme_sig_notation): Add field 'unprotected'. * src/sig-notation.c (sig_notation_set_flags): Set flag. * src/engine-gpgsm.c (gpgsm_sign): Implement setting of notations. * tests/run-support.h: Add conversion macros. * tests/run-sign.c (struct mystringlist_s): New. (xmalloc): New. (xstrtokenize): New. (print_result): Add arg fp and adjust callers. (main): Add options --binary and --notation.
-rw-r--r--doc/gpgme.texi23
-rw-r--r--src/engine-gpgsm.c43
-rw-r--r--src/gpgme.h.in6
-rw-r--r--src/sig-notation.c1
-rw-r--r--tests/run-sign.c185
-rw-r--r--tests/run-support.h4
6 files changed, 246 insertions, 16 deletions
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index 7334d62a..07a47306 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -6175,6 +6175,13 @@ notation data is in human readable form
The @code{GPGME_SIG_NOTATION_CRITICAL} symbol specifies that the
notation data is critical.
+@item GPGME_SIG_NOTATION_UNPROTECTED
+@since{2.1.0}
+
+The @code{GPGME_SIG_NOTATION_UNPROTECTED} symbol is used with CMS
+signatures to indicate that an attribute is unsigned or shall be
+created as unsigned.
+
@end table
@item unsigned int human_readable : 1
@@ -6186,6 +6193,10 @@ not for policy URLs.
This is true if the @code{GPGME_SIG_NOTATION_CRITICAL} flag is set and
false otherwise. This flag is valid for notation data and policy URLs.
+@item unsigned int unprotected : 1
+This is true if the @code{GPGME_SIG_NOTATION_UNPROTECTED} flag is set and
+false otherwise. This flag is currently only used for CMS attributes.
+
@end table
@end deftp
@@ -6711,7 +6722,8 @@ context.
Using the following functions, you can attach arbitrary notation data
to a signature. This information is then available to the user when
-the signature is verified.
+the signature is verified. The functions may also be used for CMS to
+add signed or unsigned attributes to data signatures.
@deftypefun void gpgme_sig_notation_clear (@w{gpgme_ctx_t @var{ctx}})
@since{1.1.0}
@@ -6742,6 +6754,15 @@ strings must be in UTF-8 encoding.
If @var{name} is @code{NULL}, then @var{value} should be a policy URL.
+If this function is used with a GPGME_PROTOCOL_CMS, @var{name} gives
+the OID in decimal-dotted format or it may be one of the system
+attributes denoted by a symbol name prefixed with an underscore
+(e.g. @code{_signingCertificateV2}). @var{value} needs to be the
+DER value for the attribute encoded in hexified format; for
+a system attribute it may be @code{NULL}. The flag
+@code{GPGME_SIG_NOTATION_UNPROTECTED} can be used to create an
+unsigned attribute.
+
The function @code{gpgme_sig_notation_add} returns the error code
@code{GPG_ERR_NO_ERROR} if the notation data could be added
successfully, @code{GPG_ERR_INV_VALUE} if @var{ctx} is not a valid
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index 94edab6b..21d6329b 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -2243,6 +2243,8 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
char *assuan_cmd;
int i;
gpgme_key_t key;
+ gpgme_sig_notation_t notation;
+ int any_notations;
(void)use_textmode;
@@ -2275,6 +2277,47 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
return err;
}
+ /* Setup attributes. */
+ any_notations = 0;
+ for (notation = gpgme_sig_notation_get (ctx);
+ notation; notation = notation->next)
+ {
+ if (!notation->name || !*notation->name)
+ continue; /* We always require a name. */
+ if (*notation->name == '_')
+ {
+ /* System attribute - use verbatim. */
+ assuan_cmd = _gpgme_strconcat
+ (!any_notations? "SETATTR --clear ": "SETATTR ",
+ notation->name, NULL);
+ if (!assuan_cmd)
+ return gpg_error_from_syserror ();
+ }
+ else if (!notation->value || !*notation->value)
+ {
+ /* Note that a regular attribute requires a value. */
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ else
+ {
+ /* FIXME: Handle long attribute values. */
+ assuan_cmd = _gpgme_strconcat
+ (!any_notations? "SETATTR --clear ": "SETATTR ",
+ notation->name,
+ notation->flags & GPGME_SIG_NOTATION_UNPROTECTED? ":u:":":s:",
+ notation->value, NULL);
+ if (!assuan_cmd)
+ return gpg_error_from_syserror ();
+ }
+ err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
+ free (assuan_cmd);
+ assuan_cmd = NULL;
+ if (err)
+ return err;
+ any_notations = 1; /* Do not use --clear the next round. */
+ }
+
+
for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
{
const char *s = key->subkeys ? key->subkeys->fpr : NULL;
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index c8748eb7..bfc9f490 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -428,6 +428,7 @@ typedef unsigned int gpgme_export_mode_t;
/* The available signature notation flags. */
#define GPGME_SIG_NOTATION_HUMAN_READABLE 1
#define GPGME_SIG_NOTATION_CRITICAL 2
+#define GPGME_SIG_NOTATION_UNPROTECTED 4
typedef unsigned int gpgme_sig_notation_flags_t;
@@ -508,8 +509,11 @@ struct _gpgme_sig_notation
/* Notation data is critical. */
unsigned int critical : 1;
+ /* Notaion data is unprotected (unsigned). */
+ unsigned int unprotected : 1;
+
/* Internal to GPGME, do not use. */
- int _unused : 30;
+ int _unused : 29;
};
diff --git a/src/sig-notation.c b/src/sig-notation.c
index bebf426e..30573ca2 100644
--- a/src/sig-notation.c
+++ b/src/sig-notation.c
@@ -58,6 +58,7 @@ sig_notation_set_flags (gpgme_sig_notation_t notation,
accessible individually for the user. */
notation->human_readable = flags & GPGME_SIG_NOTATION_HUMAN_READABLE ? 1 : 0;
notation->critical = flags & GPGME_SIG_NOTATION_CRITICAL ? 1 : 0;
+ notation->unprotected = flags & GPGME_SIG_NOTATION_UNPROTECTED ? 1 : 0;
notation->flags = flags;
}
diff --git a/tests/run-sign.c b/tests/run-sign.c
index c04fa819..4729e4bd 100644
--- a/tests/run-sign.c
+++ b/tests/run-sign.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <assert.h>
#include <gpgme.h>
@@ -37,6 +38,98 @@
static int verbose;
+
+struct mystringlist_s
+{
+ struct mystringlist_s *next;
+ char d[1];
+};
+typedef struct mystringlist_s *mystringlist_t;
+
+
+static void *
+xmalloc (size_t n)
+{
+ char *p = malloc (n);
+ if (!p)
+ {
+ fprintf (stderr, "malloc failed\n");
+ exit (2);
+ }
+ return p;
+}
+
+
+/* Tokenize STRING using the set of delimiters in DELIM into a NULL
+ * delimited array. Leading spaces and tabs are removed from all
+ * tokens if TRIM is set. The caller must free the result.
+ *
+ * Returns: A malloced and NULL delimited array with the tokens. On
+ * memory error the process is terminated.
+ */
+static char **
+xstrtokenize (const char *string, const char *delim, int trim)
+{
+ const char *s;
+ size_t fields;
+ size_t bytes, n;
+ char *buffer;
+ char *p, *px, *pend;
+ char **result;
+
+ /* Count the number of fields. */
+ for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim))
+ fields++;
+ fields++; /* Add one for the terminating NULL. */
+
+ /* Allocate an array for all fields, a terminating NULL, and space
+ for a copy of the string. */
+ bytes = fields * sizeof *result;
+ if (bytes / sizeof *result != fields)
+ {
+ fprintf (stderr, "malloc would overflow\n");
+ exit (2);
+ }
+ n = strlen (string) + 1;
+ bytes += n;
+ if (bytes < n)
+ {
+ fprintf (stderr, "malloc would overflow\n");
+ exit (2);
+ }
+ result = xmalloc (bytes);
+ buffer = (char*)(result + fields);
+
+ /* Copy and parse the string. */
+ strcpy (buffer, string);
+ for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1)
+ {
+ *pend = 0;
+ if (trim)
+ {
+ while (spacep (p))
+ p++;
+ for (px = pend - 1; px >= p && spacep (px); px--)
+ *px = 0;
+ }
+ result[n++] = p;
+ }
+ if (trim)
+ {
+ while (spacep (p))
+ p++;
+ for (px = p + strlen (p) - 1; px >= p && spacep (px); px--)
+ *px = 0;
+ }
+ result[n++] = p;
+ result[n] = NULL;
+
+ assert ((char*)(result + n + 1) == buffer);
+
+ return result;
+}
+
+
static gpg_error_t
status_cb (void *opaque, const char *keyword, const char *value)
{
@@ -47,7 +140,7 @@ status_cb (void *opaque, const char *keyword, const char *value)
static void
-print_result (gpgme_sign_result_t result, gpgme_sig_mode_t type)
+print_result (FILE *fp, gpgme_sign_result_t result, gpgme_sig_mode_t type)
{
gpgme_invalid_key_t invkey;
gpgme_new_signature_t sig;
@@ -55,18 +148,18 @@ print_result (gpgme_sign_result_t result, gpgme_sig_mode_t type)
(void)type;
for (invkey = result->invalid_signers; invkey; invkey = invkey->next)
- printf ("Signing key `%s' not used: %s <%s>\n",
- nonnull (invkey->fpr),
- gpg_strerror (invkey->reason), gpg_strsource (invkey->reason));
+ fprintf (fp, "Signing key `%s' not used: %s <%s>\n",
+ nonnull (invkey->fpr),
+ gpg_strerror (invkey->reason), gpg_strsource (invkey->reason));
for (sig = result->signatures; sig; sig = sig->next)
{
- printf ("Key fingerprint: %s\n", nonnull (sig->fpr));
- printf ("Signature type : %d\n", sig->type);
- printf ("Public key algo: %d\n", sig->pubkey_algo);
- printf ("Hash algo .....: %d\n", sig->hash_algo);
- printf ("Creation time .: %ld\n", sig->timestamp);
- printf ("Sig class .....: 0x%u\n", sig->sig_class);
+ fprintf (fp, "Key fingerprint: %s\n", nonnull (sig->fpr));
+ fprintf (fp, "Signature type : %d\n", sig->type);
+ fprintf (fp, "Public key algo: %d\n", sig->pubkey_algo);
+ fprintf (fp, "Hash algo .....: %d\n", sig->hash_algo);
+ fprintf (fp, "Creation time .: %ld\n", sig->timestamp);
+ fprintf (fp, "Sig class .....: 0x%u\n", sig->sig_class);
}
}
@@ -92,7 +185,9 @@ show_usage (int ex)
" --archive create a signed archive with the given file or directory\n"
" --directory DIR switch to directory DIR before creating the archive\n"
" --output FILE write output to FILE instead of stdout\n"
+ " --binary output in binary format\n"
" --diagnostics print diagnostics\n"
+ " --notation STRG Use notation/attribute specified by STRG\n"
, stderr);
exit (ex);
}
@@ -116,8 +211,11 @@ main (int argc, char **argv)
int include_key_block = 0;
int diagnostics = 0;
int direct_file_io = 0;
+ int use_binary = 0;
const char *sender = NULL;
const char *s;
+ mystringlist_t notations = NULL;
+ mystringlist_t sl;
if (argc)
{ argc--; argv++; }
@@ -224,6 +322,22 @@ main (int argc, char **argv)
diagnostics = 1;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--binary"))
+ {
+ use_binary = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--notation"))
+ {
+ argc--; argv++;
+ if (!argc)
+ show_usage (1);
+ sl = xmalloc (sizeof *sl + strlen (*argv));
+ strcpy (sl->d, *argv);
+ sl->next = notations;
+ notations = sl;
+ argc--; argv++;
+ }
else if (!strncmp (*argv, "--", 2))
show_usage (1);
@@ -243,12 +357,53 @@ main (int argc, char **argv)
err = gpgme_new (&ctx);
fail_if_err (err);
gpgme_set_protocol (ctx, protocol);
- gpgme_set_armor (ctx, 1);
+ gpgme_set_armor (ctx, !use_binary);
if (print_status)
gpgme_set_status_cb (ctx, status_cb, NULL);
if (use_loopback)
gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK);
+ /* Parse and set notations. */
+ for (sl = notations; sl; sl = sl->next)
+ {
+ char **fields = xstrtokenize (sl->d, ":", 1);
+
+ if (fields[0] && fields[0][0] == '_')
+ {
+ if (fields[1])
+ {
+ fprintf (stderr, PGM ": notation '%s': syntax error\n", sl->d);
+ exit (1);
+ }
+ err = gpgme_sig_notation_add (ctx, fields[0], NULL, 0);
+ }
+ else
+ {
+ if (!fields[0] || !fields[1] || !fields[2] || fields[3])
+ {
+ fprintf (stderr, PGM ": notation '%s': syntax error\n", sl->d);
+ exit (1);
+ }
+ if (protocol == GPGME_PROTOCOL_CMS
+ && !(!strcmp (fields[1], "u") || !strcmp (fields[1], "s")))
+ {
+ fprintf (stderr, PGM ": notation '%s':"
+ " second field must be 'u' or 's'\n", sl->d);
+ exit (1);
+ }
+ err = gpgme_sig_notation_add (ctx, fields[0], fields[2],
+ !strcmp (fields[1], "u")?
+ GPGME_SIG_NOTATION_UNPROTECTED : 0);
+ }
+ if (err)
+ {
+ fprintf (stderr, PGM ": error setting notation '%s': %s\n",
+ sl->d, gpg_strerror (err));
+ exit (1);
+ }
+ free (fields);
+ }
+
if (key_string)
{
gpgme_key_t akey;
@@ -345,7 +500,7 @@ main (int argc, char **argv)
}
if (result)
- print_result (result, sigmode);
+ print_result (use_binary? stderr: stdout, result, sigmode);
if (err)
{
fprintf (stderr, PGM ": signing failed: %s\n", gpg_strerror (err));
@@ -357,9 +512,11 @@ main (int argc, char **argv)
if (!output)
{
- fputs ("Begin Output:\n", stdout);
+ if (!use_binary)
+ fputs ("Begin Output:\n", stdout);
print_data (out);
- fputs ("End Output.\n", stdout);
+ if (!use_binary)
+ fputs ("End Output.\n", stdout);
}
gpgme_data_release (out);
diff --git a/tests/run-support.h b/tests/run-support.h
index 6471e2b9..220835d2 100644
--- a/tests/run-support.h
+++ b/tests/run-support.h
@@ -59,6 +59,10 @@
} \
while (0)
+#define atoi_1(p) (*(p) - '0' )
+#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
+#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
+#define spacep(p) (*(p) == ' ' || *(p) == '\t')
static const char *
nonnull (const char *s)