Split GPG interface into its own helper library

This mostly moves existing code from builtin/tag.c (for signing) and builtin/verify-tag.c (for verifying) to a new gpg-interface.c file to provide a more generic library interface. - sign_buffer() takes a payload strbuf, a signature strbuf, and a signing key, runs "gpg" to produce a detached signature for the payload, and appends it to the signature strbuf. The contents of a signed tag that concatenates the payload and the detached signature can be produced by giving the same strbuf as payload and signature strbuf. - verify_signed_buffer() takes a payload and a detached signature as <ptr, len> pairs, and runs "gpg --verify" to see if the payload matches the signature. It can optionally capture the output from GPG to allow the callers to pretty-print it in a way more suitable for their contexts. "verify-tag" (aka "tag -v") used to save the whole tag contents as if it is a detached signature, and fed gpg the payload part of the tag. It relied on gpg to fail when the given tag is not signed but just is annotated. The updated run_gpg_verify() function detects the lack of detached signature in the input, and errors out without bothering "gpg". Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Sep 7, 2011 at 21:19 UTC 2f47eae2a1337bd0cb50dbc936df6433f460bb4a
6 files changed +166 -100
Makefile
+2
@@ -528,6 +528,7 @@ LIB_H += exec_cmd.h
528 LIB_H += fsck.h
529 LIB_H += gettext.h
530 LIB_H += git-compat-util.h
531 +LIB_H += gpg-interface.h
532 LIB_H += graph.h
533 LIB_H += grep.h
534 LIB_H += hash.h
@@ -621,6 +622,7 @@ LIB_OBJS += entry.o
622 LIB_OBJS += environment.o
623 LIB_OBJS += exec_cmd.o
624 LIB_OBJS += fsck.o
625 +LIB_OBJS += gpg-interface.o
626 LIB_OBJS += graph.o
627 LIB_OBJS += grep.o
628 LIB_OBJS += hash.o
builtin/tag.c
+6 -70
@@ -14,6 +14,7 @@
14 #include "parse-options.h"
15 #include "diff.h"
16 #include "revision.h"
17 +#include "gpg-interface.h"
18
19 static const char * const git_tag_usage[] = {
20 "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -23,8 +24,6 @@ static const char * const git_tag_usage[] = {
24 NULL
25 };
26
26 -static char signingkey[1000];
27 -
27 struct tag_filter {
28 const char **patterns;
29 int lines;
@@ -208,60 +207,7 @@ static int verify_tag(const char *name, const char *ref,
207
208 static int do_sign(struct strbuf *buffer)
209 {
211 - struct child_process gpg;
212 - const char *args[4];
213 - char *bracket;
214 - int len;
215 - int i, j;
216 -
217 - if (!*signingkey) {
218 - if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
219 - sizeof(signingkey)) > sizeof(signingkey) - 1)
220 - return error(_("committer info too long."));
221 - bracket = strchr(signingkey, '>');
222 - if (bracket)
223 - bracket[1] = '\0';
224 - }
225 -
226 - /* When the username signingkey is bad, program could be terminated
227 - * because gpg exits without reading and then write gets SIGPIPE. */
228 - signal(SIGPIPE, SIG_IGN);
229 -
230 - memset(&gpg, 0, sizeof(gpg));
231 - gpg.argv = args;
232 - gpg.in = -1;
233 - gpg.out = -1;
234 - args[0] = "gpg";
235 - args[1] = "-bsau";
236 - args[2] = signingkey;
237 - args[3] = NULL;
238 -
239 - if (start_command(&gpg))
240 - return error(_("could not run gpg."));
241 -
242 - if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
243 - close(gpg.in);
244 - close(gpg.out);
245 - finish_command(&gpg);
246 - return error(_("gpg did not accept the tag data"));
247 - }
248 - close(gpg.in);
249 - len = strbuf_read(buffer, gpg.out, 1024);
250 - close(gpg.out);
251 -
252 - if (finish_command(&gpg) || !len || len < 0)
253 - return error(_("gpg failed to sign the tag"));
254 -
255 - /* Strip CR from the line endings, in case we are on Windows. */
256 - for (i = j = 0; i < buffer->len; i++)
257 - if (buffer->buf[i] != '\r') {
258 - if (i != j)
259 - buffer->buf[j] = buffer->buf[i];
260 - j++;
261 - }
262 - strbuf_setlen(buffer, j);
263 -
264 - return 0;
210 + return sign_buffer(buffer, buffer, get_signing_key());
211 }
212
213 static const char tag_template[] =
@@ -270,21 +216,11 @@ static const char tag_template[] =
216 "# Write a tag message\n"
217 "#\n");
218
273 -static void set_signingkey(const char *value)
274 -{
275 - if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
276 - die(_("signing key value too long (%.10s...)"), value);
277 -}
278 -
219 static int git_tag_config(const char *var, const char *value, void *cb)
220 {
281 - if (!strcmp(var, "user.signingkey")) {
282 - if (!value)
283 - return config_error_nonbool(var);
284 - set_signingkey(value);
285 - return 0;
286 - }
287 -
221 + int status = git_gpg_config(var, value, cb);
222 + if (status)
223 + return status;
224 return git_default_config(var, value, cb);
225 }
226
@@ -463,7 +399,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
399
400 if (keyid) {
401 sign = 1;
466 - set_signingkey(keyid);
402 + set_signing_key(keyid);
403 }
404 if (sign)
405 annotate = 1;
builtin/verify-tag.c
+5 -30
@@ -11,6 +11,7 @@
11 #include "run-command.h"
12 #include <signal.h>
13 #include "parse-options.h"
14 +#include "gpg-interface.h"
15
16 static const char * const verify_tag_usage[] = {
17 "git verify-tag [-v|--verbose] <tag>...",
@@ -19,42 +20,16 @@ static const char * const verify_tag_usage[] = {
20
21 static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
22 {
22 - struct child_process gpg;
23 - const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
24 - char path[PATH_MAX];
25 - size_t len;
26 - int fd, ret;
23 + int len;
24
28 - fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
29 - if (fd < 0)
30 - return error("could not create temporary file '%s': %s",
31 - path, strerror(errno));
32 - if (write_in_full(fd, buf, size) < 0)
33 - return error("failed writing temporary file '%s': %s",
34 - path, strerror(errno));
35 - close(fd);
36 -
37 - /* find the length without signature */
25 len = parse_signature(buf, size);
26 if (verbose)
27 write_in_full(1, buf, len);
28
42 - memset(&gpg, 0, sizeof(gpg));
43 - gpg.argv = args_gpg;
44 - gpg.in = -1;
45 - args_gpg[2] = path;
46 - if (start_command(&gpg)) {
47 - unlink(path);
48 - return error("could not run gpg.");
49 - }
50 -
51 - write_in_full(gpg.in, buf, len);
52 - close(gpg.in);
53 - ret = finish_command(&gpg);
29 + if (size == len)
30 + return error("no signature found");
31
55 - unlink_or_warn(path);
56 -
57 - return ret;
32 + return verify_signed_buffer(buf, len, buf + len, size - len, NULL);
33 }
34
35 static int verify_tag(const char *name, int verbose)
gpg-interface.c new
+138
@@ -0,0 +1,138 @@
1 +#include "cache.h"
2 +#include "run-command.h"
3 +#include "strbuf.h"
4 +#include "gpg-interface.h"
5 +#include "sigchain.h"
6 +
7 +static char *configured_signing_key;
8 +
9 +void set_signing_key(const char *key)
10 +{
11 + free(configured_signing_key);
12 + configured_signing_key = xstrdup(key);
13 +}
14 +
15 +int git_gpg_config(const char *var, const char *value, void *cb)
16 +{
17 + if (!strcmp(var, "user.signingkey")) {
18 + if (!value)
19 + return config_error_nonbool(var);
20 + set_signing_key(value);
21 + }
22 + return 0;
23 +}
24 +
25 +const char *get_signing_key(void)
26 +{
27 + if (configured_signing_key)
28 + return configured_signing_key;
29 + return git_committer_info(IDENT_ERROR_ON_NO_NAME|IDENT_NO_DATE);
30 +}
31 +
32 +/*
33 + * Create a detached signature for the contents of "buffer" and append
34 + * it after "signature"; "buffer" and "signature" can be the same
35 + * strbuf instance, which would cause the detached signature appended
36 + * at the end.
37 + */
38 +int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
39 +{
40 + struct child_process gpg;
41 + const char *args[4];
42 + ssize_t len;
43 + size_t i, j, bottom;
44 +
45 + memset(&gpg, 0, sizeof(gpg));
46 + gpg.argv = args;
47 + gpg.in = -1;
48 + gpg.out = -1;
49 + args[0] = "gpg";
50 + args[1] = "-bsau";
51 + args[2] = signing_key;
52 + args[3] = NULL;
53 +
54 + if (start_command(&gpg))
55 + return error(_("could not run gpg."));
56 +
57 + /*
58 + * When the username signingkey is bad, program could be terminated
59 + * because gpg exits without reading and then write gets SIGPIPE.
60 + */
61 + sigchain_push(SIGPIPE, SIG_IGN);
62 +
63 + if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
64 + close(gpg.in);
65 + close(gpg.out);
66 + finish_command(&gpg);
67 + return error(_("gpg did not accept the data"));
68 + }
69 + close(gpg.in);
70 +
71 + bottom = signature->len;
72 + len = strbuf_read(signature, gpg.out, 1024);
73 + close(gpg.out);
74 +
75 + sigchain_pop(SIGPIPE);
76 +
77 + if (finish_command(&gpg) || !len || len < 0)
78 + return error(_("gpg failed to sign the data"));
79 +
80 + /* Strip CR from the line endings, in case we are on Windows. */
81 + for (i = j = bottom; i < signature->len; i++)
82 + if (signature->buf[i] != '\r') {
83 + if (i != j)
84 + signature->buf[j] = signature->buf[i];
85 + j++;
86 + }
87 + strbuf_setlen(signature, j);
88 +
89 + return 0;
90 +}
91 +
92 +/*
93 + * Run "gpg" to see if the payload matches the detached signature.
94 + * gpg_output_to tells where the output from "gpg" should go:
95 + * < 0: /dev/null
96 + * = 0: standard error of the calling process
97 + * > 0: the specified file descriptor
98 + */
99 +int verify_signed_buffer(const char *payload, size_t payload_size,
100 + const char *signature, size_t signature_size,
101 + struct strbuf *gpg_output)
102 +{
103 + struct child_process gpg;
104 + const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
105 + char path[PATH_MAX];
106 + int fd, ret;
107 +
108 + fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
109 + if (fd < 0)
110 + return error("could not create temporary file '%s': %s",
111 + path, strerror(errno));
112 + if (write_in_full(fd, signature, signature_size) < 0)
113 + return error("failed writing detached signature to '%s': %s",
114 + path, strerror(errno));
115 + close(fd);
116 +
117 + memset(&gpg, 0, sizeof(gpg));
118 + gpg.argv = args_gpg;
119 + gpg.in = -1;
120 + if (gpg_output)
121 + gpg.err = -1;
122 + args_gpg[2] = path;
123 + if (start_command(&gpg)) {
124 + unlink(path);
125 + return error("could not run gpg.");
126 + }
127 +
128 + write_in_full(gpg.in, payload, payload_size);
129 + close(gpg.in);
130 +
131 + if (gpg_output)
132 + strbuf_read(gpg_output, gpg.err, 0);
133 + ret = finish_command(&gpg);
134 +
135 + unlink_or_warn(path);
136 +
137 + return ret;
138 +}
gpg-interface.h new
+10
@@ -0,0 +1,10 @@
1 +#ifndef GPG_INTERFACE_H
2 +#define GPG_INTERFACE_H
3 +
4 +extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
5 +extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output);
6 +extern int git_gpg_config(const char *, const char *, void *);
7 +extern void set_signing_key(const char *);
8 +extern const char *get_signing_key(void);
9 +
10 +#endif
tag.c
+5
@@ -139,6 +139,11 @@ int parse_tag(struct tag *item)
139 return ret;
140 }
141
142 +/*
143 + * Look at a signed tag object, and return the offset where
144 + * the embedded detached signature begins, or the end of the
145 + * data when there is no such signature.
146 + */
147 size_t parse_signature(const char *buf, unsigned long size)
148 {
149 char *eol;