{"thread":{"id":"51679","subject":"[RFC PATCH 2/5] Signing API: Added new signing interface API","startedAt":"2019-08-19T21:19:47Z","lastAt":"2019-08-19T21:19:47Z","messageCount":1,"participants":["Ibrahim El"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"380732","messageId":"8AMhjK19PJ35u3LCR57IvtAzOBN5bKK2vUn0Ns-4mmZzK9U14W5CGW5R8aITNXBm78J4Z7nd09RTVKW2pGaB4PnF7p2PireF_vzRST8DngE=@pm.me","threadId":"51679","inReplyTo":null,"subject":"[RFC PATCH 2/5] Signing API: Added new signing interface API","fromName":"Ibrahim El","fromEmail":"ibrahim.el@pm.me","sentAt":"2019-08-19T21:19:28Z","receivedAt":"2019-08-19T21:19:47Z","isPatch":true,"sender":{"key":"ibrahim.el@pm.me","avatar":null},"body":"From: Ibrahim El Rhezzali <ibrahim.el@pm.me>\n\n7e3e6c9e4 Added new signing interface API\n\nAdding files for the new signing interface and also support drivers for the two existing GPG and GPGSM X.509 tools\n\nSigned-off-by: Ibrahim El <ibrahim.el@pm.me>\n---\n Makefile               |   3 +\n signing-interface.c    | 487 +++++++++++++++++++++++++++++++++++++++++++++++++\n signing-interface.h    | 151 +++++++++++++++\n signing-tool-openpgp.c | 409 +++++++++++++++++++++++++++++++++++++++++\n signing-tool-x509.c    | 383 ++++++++++++++++++++++++++++++++++++++\n signing-tool.h         |  35 ++++\n 6 files changed, 1468 insertions(+)\n create mode 100644 signing-interface.c\n create mode 100644 signing-interface.h\n create mode 100644 signing-tool-openpgp.c\n create mode 100644 signing-tool-x509.c\n create mode 100644 signing-tool.h\n\ndiff --git a/Makefile b/Makefile\nindex f58bf14c7..244540e8d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -978,6 +978,9 @@ LIB_OBJS += sha1-name.o\n LIB_OBJS += shallow.o\n LIB_OBJS += sideband.o\n LIB_OBJS += sigchain.o\n+LIB_OBJS += signing-interface.o\n+LIB_OBJS += signing-tool-openpgp.o\n+LIB_OBJS += signing-tool-x509.o\n LIB_OBJS += split-index.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += streaming.o\ndiff --git a/signing-interface.c b/signing-interface.c\nnew file mode 100644\nindex 000000000..c744ef499\n--- /dev/null\n+++ b/signing-interface.c\n@@ -0,0 +1,487 @@\n+#include <sys/types.h>\n+#include <unistd.h>\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+#include \"strbuf.h\"\n+#include \"signing-interface.h\"\n+#include \"signing-tool.h\"\n+#include \"sigchain.h\"\n+#include \"tempfile.h\"\n+\n+extern const struct signing_tool openpgp_tool;\n+extern const struct signing_tool x509_tool;\n+\n+static const struct signing_tool *signing_tools[SIGNATURE_TYPE_COUNT] = {\n+\t&openpgp_tool,\n+\t&x509_tool,\n+};\n+\n+enum signature_type default_type = SIGNATURE_TYPE_DEFAULT;\n+static const char* unknown_signature_type = \"unknown signature type\";\n+static char* default_signing_key = NULL;\n+\n+static void add_signature(struct signatures *sigs, struct signature *sig) {\n+\tif (!sigs || !sig)\n+\t\treturn;\n+\tALLOC_GROW(sigs->sigs, sigs->nsigs + 1, sigs->alloc);\n+\tsigs->sigs[sigs->nsigs++] = sig;\n+}\n+\n+void signatures_clear(struct signatures *sigs)\n+{\n+\tsize_t i;\n+\tstruct signature *psig;\n+\n+\tif (!sigs) return;\n+\t\n+\tfor (i = 0; i < sigs->nsigs; i++) {\n+\t\tpsig = sigs->sigs[i];\n+\t\tstrbuf_release(&(psig->sig));\n+\t\tstrbuf_release(&(psig->output));\n+\t\tstrbuf_release(&(psig->status));\n+\t\tFREE_AND_NULL(psig->signer);\n+\t\tFREE_AND_NULL(psig->key);\n+\t\tFREE_AND_NULL(psig->fingerprint);\n+\t\tFREE_AND_NULL(psig->key);\n+\t\tFREE_AND_NULL(psig);\n+\t}\n+\tFREE_AND_NULL(sigs->sigs);\n+\tsigs->nsigs = 0;\n+\tsigs->alloc = 0;\n+}\n+\n+void signature_clear(struct signature *sigc)\n+{\n+\tFREE_AND_NULL(sigc->sig.buf);\n+\tFREE_AND_NULL(sigc->output.buf);\n+\tFREE_AND_NULL(sigc->status.buf);\n+\tFREE_AND_NULL(sigc->signer);\n+\tFREE_AND_NULL(sigc->key);\n+\tFREE_AND_NULL(sigc->fingerprint);\n+\tFREE_AND_NULL(sigc->primary_key_fingerprint);\n+}\n+\n+int sign_payload(const char *payload, size_t size, struct signatures *sigs,\n+\t\tenum signature_type st, const char *signing_key)\n+{\n+\tconst struct signing_tool *tool;\n+\tstruct signature *psig = xmalloc(sizeof(struct signature));\n+\tint ret;\n+\n+\tfflush(stdout);\n+\n+\tif (!sigs)\n+\t\treturn error(\"invalid signatures passed to sign function\");\n+\n+\tif (!VALID_SIGNATURE_TYPE(st))\n+\t\treturn error(\"unsupported signature type: %d\", st);\n+\n+\ttool = signing_tools[st];\n+\n+\tif (!tool || !tool->sign)\n+\t\tBUG(\"signing tool %s undefined\", signature_type_name(st));\n+\n+\tret = tool->sign(payload, size, &psig, signing_key);\n+\tif (!ret)\n+\t\tadd_signature(sigs, psig);\n+\telse\n+\n+\t\treturn error(\"signing operation failed\");\n+\n+\treturn 0;\n+}\n+\n+int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)\n+{\n+\tstruct signatures sigs = SIGNATURES_INIT;\n+\tenum signature_type st = default_type;\n+\n+\tint ret = sign_payload(buffer->buf, buffer->len, &sigs, st, signing_key);\n+\n+\tif (!ret)\n+\t{\n+\t\tstrbuf_addstr(signature, sigs.sigs[0]->sig.buf);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+size_t parse_signatures(const char *payload, size_t size, \n+\t\tstruct signatures *sigs)\n+{\n+\tenum signature_type st;\n+\tsize_t first;\n+\tsize_t begin = 0;\n+\tconst struct signing_tool *tool;\n+\tstruct signature *psig = NULL;\n+\n+\tfirst = size;\n+\tfor (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++) {\n+\t\ttool = signing_tools[st];\n+\n+\t\tif (!tool || !tool->parse)\n+\t\t\tBUG(\"signing tool %s undefined\", signature_type_name(st));\n+\n+\t\tbegin = tool->parse(payload, size, &psig);\n+\t\tif (begin < size) {\n+\t\t\tif (sigs)\n+\t\t\t\tadd_signature(sigs, psig);\n+\t\t\telse\n+\t\t\t\tFREE_AND_NULL(psig);\n+\n+\t\t\tfirst = begin;\n+\t\t\tcontinue;\n+\t\t}\n+\t}\n+\n+\treturn first;\n+}\n+\n+size_t parse_signature(const char *buf, size_t size)\n+{\n+\tsize_t match;\n+\tstruct signatures sigs = SIGNATURES_INIT;\n+\n+\tif ( !buf || !size )\n+\t\treturn size;\n+\n+\tmatch = parse_signatures(buf, size, &sigs);\n+\n+\treturn match;\n+}\n+\n+int verify_buffer_signatures(const char *payload, size_t size,\n+\t\tstruct signatures *sigs)\n+{\n+\tint ret = 0;\n+\tsize_t i;\n+\tconst struct signing_tool *tool;\n+\tstruct signature *psig;\n+\n+\tif (!sigs)\n+\t\terror(\"invalid signatures passed to verify function\");\n+\n+\tfor (i = 0; i < sigs->nsigs; i++) {\n+\t\tpsig = sigs->sigs[i];\n+\t\ttool = signing_tools[psig->st];\n+\n+\t\tif (!tool || !tool->verify)\n+\t\t\tBUG(\"signing tool %s undefined\", signature_type_name(psig->st));\n+\n+\t\tret |= tool->verify(payload, size, psig);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+int verify_signed_buffer(const char *payload, size_t payload_size,\n+\t\t\t const char *signature, size_t signature_size,\n+\t\t\t struct strbuf *output, struct strbuf *status)\n+{\n+\tint ret;\n+\tenum signature_type st;\n+\tstruct signature sig = SIGNATURE_INIT;\n+\tstruct signatures sigs = SIGNATURES_INIT;\n+\n+\tif ( !payload || !signature )\n+\t\treturn error(\"invalid payload or signature sent !\");\n+\n+\tstrbuf_addstr(&(sig.sig), signature);\n+\tadd_signature(&sigs, &sig);\n+\n+\tret = verify_buffer_signatures(payload, payload_size, &sigs);\n+\n+\t/*  Some how gpg.format is not sometimes applied, temporary fix to loop and STs */\n+\tif (ret)\n+\t{\n+\t\tfor (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++)\n+\t\t{\n+\t\t\tsig.st = st;\n+\t\t\tret = verify_buffer_signatures(payload, payload_size, &sigs);\n+\t\t\tif (!ret || sig.result != '0')\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (output)\n+\t\tstrbuf_addstr(output, sig.output.buf);\n+\tif (status)\n+\t\tstrbuf_addstr(status, sig.status.buf);\n+\n+\treturn ret;\n+}\n+\n+int check_signature(const char *payload, size_t plen, const char *signature,\n+\tsize_t slen, struct signature *sigc)\n+{\n+\tint status;\n+\tenum signature_type st;\n+\tstruct signatures sigs = SIGNATURES_INIT;\n+\tstruct signature sig = SIGNATURE_INIT;\n+\t\n+\tif (!payload || !signature || !sigc)\n+\t\tBUG(\"invalid payload or signature sent !\");\n+\n+\tstrbuf_addstr(&(sig.sig), signature);\n+\tsig.result = 'N';\n+\tsig.st = default_type;\n+\n+\tadd_signature(&sigs, &sig);\n+\n+\tstatus = verify_buffer_signatures(payload, plen, &sigs);\n+\n+\t/*  Some how gpg.format is not sometimes applied, temporary fix to loop and STs */\n+\tif (status)\n+\t{\n+\t\tfor (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++)\n+\t\t{\n+\t\t\tsig.st = st;\n+\t\t\tstatus = verify_buffer_signatures(payload, plen, &sigs);\n+\t\t\tif (!status || sig.result != 'N')\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\tstatus |= sig.result != 'G' && sig.result != 'U';\n+\n+\tif (sig.signer && !sigc->signer)\n+\t\tsigc->signer = xstrdup(sig.signer);\n+\tif (sig.key && !sigc->key)\n+\t\tsigc->key = xstrdup(sig.key);\n+\tif (sig.fingerprint && !sigc->fingerprint)\n+\t\tsigc->fingerprint = xstrdup(sig.fingerprint);\n+\tif (sig.primary_key_fingerprint && !sigc->primary_key_fingerprint)\n+\t\tsigc->primary_key_fingerprint = xstrdup(sig.primary_key_fingerprint);\t\n+\n+\tsigc->st = sig.st;\n+\tsigc->result = sig.result;\n+\t\n+\tstrbuf_addstr(&(sigc->sig), payload);\n+\tstrbuf_addstr(&(sigc->output), sig.output.buf);\n+\tstrbuf_addstr(&(sigc->status), sig.status.buf);\n+\n+\treturn !!status;\n+}\n+\n+size_t strbuf_append_signatures(struct strbuf *buf, const struct signatures *sigs)\n+{\n+\tsize_t i;\n+\tstruct signature *psig;\n+\n+\tif (!buf)\n+\t\tBUG(\"invalid buffer passed to signature append function\");\n+\n+\tif (!sigs)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < sigs->nsigs; i++) {\n+\t\tpsig = sigs->sigs[i];\n+\t\tstrbuf_addbuf(buf, &(psig->sig));\n+\t}\n+\n+\treturn sigs->nsigs;\n+}\n+\n+void print_signatures(const struct signatures *sigs, unsigned flags)\n+{\n+\tsize_t i;\n+\tconst struct signing_tool *tool;\n+\tconst struct signature *psig;\n+\n+\tif (!sigs)\n+\t\terror(\"invalid signatures passed to verify function\");\n+\n+\tfor (i = 0; i < sigs->nsigs; i++) {\n+\t\tpsig = sigs->sigs[i];\n+\t\ttool = signing_tools[psig->st];\n+\n+\t\tif (!tool || !tool->print)\n+\t\t\tBUG(\"signing tool %s undefined\", signature_type_name(psig->st));\n+\n+\t\ttool->print(psig, flags);\n+\t}\n+}\n+\n+void print_signature_buffer(const struct signature *sigc, unsigned flags)\n+{\n+\tconst struct signing_tool *tool;\n+\n+\tif (!sigc)\n+\t\terror(\"invalid signatures passed to verify function\");\n+\n+\ttool = signing_tools[default_type];\n+\n+\tif (!tool || !tool->print)\n+\t\tBUG(\"signing tool %s undefined\", signature_type_name(sigc->st));\n+\n+\ttool->print(sigc, flags);\n+}\n+\n+enum signature_type signature_type_by_name(const char *name)\n+{\n+\tenum signature_type st;\n+\n+\tif (!name)\n+\t\treturn default_type;\n+\n+\tfor (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++)\n+\t\tif (!strcmp(signing_tools[st]->name, name))\n+\t\t\treturn st;\n+\n+\treturn error(\"unknown signature type: %s\", name);\n+}\n+\n+const char *signature_type_name(enum signature_type st)\n+{\n+\tif (!VALID_SIGNATURE_TYPE(st))\n+\t\treturn unknown_signature_type;\n+\n+\treturn signing_tools[st]->name;\n+}\n+\n+int git_signing_config(const char *var, const char *value, void *cb)\n+{\n+\tint ret = 0;\n+\tchar *t1, *t2, *t3, *buf;\n+\tenum signature_type st;\n+\tconst struct signing_tool *tool;\n+\n+\t/* user.signingkey is a deprecated alias for signing.<signing.default>.key */\n+\tif (!strcmp(var, \"user.signingkey\")) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\t\n+\t\tset_signing_key(value, default_type);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* gpg.format is a deprecated alias for signing.default */\n+\tif (!strcmp(var, \"gpg.format\") || !strcmp(var, \"signing.default\")) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\n+\t\tif (!VALID_SIGNATURE_TYPE((st = signature_type_by_name(value))))\n+\t\t\treturn config_error_nonbool(var);\n+\n+\t\tset_signature_type(st);\n+\n+\t\treturn 0;\n+\t}\n+\n+\t/* gpg.program is a deprecated alias for signing.openpgp.program */\n+\tif (!strcmp(var, \"gpg.program\") || !strcmp(var, \"signing.openpgp.program\")) {\n+\t\tret = signing_tools[OPENPGP_SIGNATURE]->config(\n+\t\t\t\t\"program\", value, cb);\n+\n+\t\treturn ret;\n+\t}\n+\n+\t/* gpg.x509.program is a deprecated alias for signing.x509.program */\n+\tif (!strcmp(var, \"gpg.x509.program\") || !strcmp(var, \"signing.x509.program\")) {\n+\t\tret = signing_tools[X509_SIGNATURE]->config(\n+\t\t\t\t\"program\", value, cb);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tbuf = xstrdup(var);\n+\tt1 = strtok(buf, \".\");\n+\tt2 = strtok(NULL, \".\");\n+\tt3 = strtok(NULL, \".\");\n+\n+\t/* gpg.<format>.* is a deprecated alias for signing.<format>.* */\n+\tif (!strcmp(t1, \"gpg\") || !strcmp(t1, \"signing\")) {\n+\t\tif (!VALID_SIGNATURE_TYPE((st = signature_type_by_name(t2)))) {\n+\t\t\tfree(buf);\n+\t\t\treturn error(\"unsupported variable: %s\", var);\n+\t\t}\n+\n+\t\ttool = signing_tools[st];\n+\t\tif (!tool || !tool->config) {\n+\t\t\tfree(buf);\n+\t\t\tBUG(\"signing tool %s undefined\", signature_type_name(tool->st));\n+\t\t}\n+\n+\t\tret = tool->config(t3, value, cb);\n+\t}\n+\n+\tfree(buf);\n+\treturn ret;\n+}\n+\n+void set_signing_key(const char *key, enum signature_type st)\n+{\n+\t/*\n+\t * Make sure we track the latest default signing key so that if the\n+\t * default signing format changes after this, we can make sure the\n+\t * default signing tool knows the key to use.\n+\t */\n+\tfree(default_signing_key);\n+\tdefault_signing_key = xstrdup(key);\n+\n+\tif (!VALID_SIGNATURE_TYPE(st))\n+\t\tsigning_tools[default_type]->set_key(key);\n+\telse\n+\t\tsigning_tools[st]->set_key(key);\n+}\n+\n+const char *get_signing_key(enum signature_type st)\n+{\n+\tif (!VALID_SIGNATURE_TYPE(st))\n+\t\treturn signing_tools[default_type]->get_key();\n+\n+\treturn signing_tools[default_type]->get_key();\n+}\n+\n+void set_signing_program(const char *signing_program, enum signature_type st)\n+{\n+\t/*\n+\t * Make sure we track the latest default signing program so that if the\n+\t * default signing format changes after this, we can make sure the\n+\t * default signing tool knows the program to use.\n+\t */\n+\n+\tif (!VALID_SIGNATURE_TYPE(st))\n+\t\tsigning_tools[default_type]->set_program(signing_program);\n+\telse\n+\t\tsigning_tools[st]->set_program(signing_program);\n+}\n+\n+const char *get_signing_program(enum signature_type st)\n+{\n+\tconst char *signing_program = NULL;\n+\n+\tif (!VALID_SIGNATURE_TYPE(st)) {\n+\t\tsigning_program = signing_tools[default_type]->get_program();\n+\n+\t\treturn signing_program;\n+\t}\n+\n+\tsigning_program = signing_tools[st]->get_program();\n+\n+\treturn signing_program;\n+}\n+\n+void set_signature_type(enum signature_type st)\n+{\n+\tif (!VALID_SIGNATURE_TYPE(st))\n+\t\treturn;\n+\n+\tdefault_type = st;\n+\n+\t/* \n+\t * If the signing key has been set, then make sure the new default\n+\t * signing tool knows about it. this fixes the order of operations\n+\t * error of parsing the default signing key and default signing\n+\t * format in arbitrary order.\n+\t */\n+\tif (default_signing_key) {\n+\t\tset_signing_key(default_signing_key, default_type);\n+\t}\n+}\n+\n+enum signature_type get_signature_type(void)\n+{\n+\treturn default_type;\n+}\n\\ No newline at end of file\ndiff --git a/signing-interface.h b/signing-interface.h\nnew file mode 100644\nindex 000000000..b55edbdb8\n--- /dev/null\n+++ b/signing-interface.h\n@@ -0,0 +1,151 @@\n+#ifndef SIGNING_INTERFACE_H\n+#define SIGNING_INTERFACE_H\n+\n+struct strbuf;\n+\n+#define OUTPUT_VERBOSE\t\t1\n+#define OUTPUT_RAW\t\t\t2\n+#define OUTPUT_OMIT_STATUS\t4\n+\n+enum signature_type {\n+\tOPENPGP_SIGNATURE,\n+\tX509_SIGNATURE,\n+\n+\tSIGNATURE_TYPE_LAST,\n+\tSIGNATURE_TYPE_FIRST = OPENPGP_SIGNATURE,\n+\tSIGNATURE_TYPE_COUNT = SIGNATURE_TYPE_LAST - SIGNATURE_TYPE_FIRST,\n+\tSIGNATURE_TYPE_DEFAULT = OPENPGP_SIGNATURE,\n+\tSIGNATURE_TYPE_UNKNOWN = -1\n+};\n+enum signature_type default_type;\n+\n+#define VALID_SIGNATURE_TYPE(x) \\\n+\t((x >= SIGNATURE_TYPE_FIRST) && (x < SIGNATURE_TYPE_LAST))\n+\n+struct signature {\n+\tstruct strbuf sig;\n+\tstruct strbuf output;\n+\tstruct strbuf status;\n+\tenum signature_type st;\n+\n+\t/*\n+\t * possible \"result\":\n+\t * 0 (not checked)\n+\t * N (checked but no further result)\n+\t * U (untrusted good)\n+\t * G (good)\n+\t * B (bad)\n+\t */\n+\tchar result;\n+\tchar *signer;\n+\tchar *key;\n+\tchar *fingerprint;\n+\tchar *primary_key_fingerprint;\n+};\n+\n+struct signatures {\n+\tsize_t nsigs;\n+\tsize_t alloc;\n+\tstruct signature **sigs;\n+};\n+\n+#define SIGNATURES_INIT  { .nsigs = 0, .alloc = 0, .sigs = NULL }\n+#define SIGNATURE_INIT  { .sig = STRBUF_INIT, .output = STRBUF_INIT, .status = STRBUF_INIT, .st = OPENPGP_SIGNATURE, .result = '0', .signer = NULL, .key = NULL }\n+\n+void signatures_clear(struct signatures *sigs);\n+void signature_clear(struct signature *sig);\n+\n+/*\n+ * Create a detached signature for the contents of \"payload\" and append\n+ * it to the list of signatures in \"sigs\". The signature type determines which\n+ * type of signature to create and the optional \"signing_key\" specifies\n+ * the key. If no signing key is specified the default key from the\n+ * config will be used. If no default is found, then an error is\n+ * returned. If the signing operation fails an error is returned.\n+ */\n+int sign_payload(const char *payload, size_t size, struct signatures *sigs,\n+\t\tenum signature_type st, const char *signing_key);\n+\n+/*\n+ * Bridge function to be called by the git code for buffer signature\n+ */\n+int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);\n+\n+/* \n+ * Look at the signed content (e.g. a signed tag object), whose payload\n+ * is followed by one or more detached signatures. Return the offset of\n+ * the first signature, or the size of the buf when there are no \n+ * signatures. If a valid signatures struct is passed in, the signatures \n+ * will be parsed and copied into its array of sigs.\n+ */\n+size_t parse_signatures(const char *payload, size_t size,\n+\t\tstruct signatures *sigs);\n+\n+/*\n+ * Bridge function to be called by the git code for parsing signatures in a buffer\n+ */\n+size_t parse_signature(const char *buf, size_t size);\n+\n+/*\n+ * Run the signature verification tools to see if the payload matches\n+ * the detached signatures. The output and status of the of the checks\n+ * is recorded in the signatures struct. The caller must use\n+ * parse_signatures or sign_buffer to initialize the signatures struct\n+ * before calling this function.\n+ */\n+int verify_signed_buffer(const char *payload, size_t payload_size,\n+\t\t\t const char *signature, size_t signature_size,\n+\t\t\t struct strbuf *output, struct strbuf *status);\n+\n+/*\n+ * Verify multiple signatures in a single buffer\n+ */\n+int verify_buffer_signatures(const char *payload, size_t size,\n+\t\tstruct signatures *sigs);\n+\n+/*\n+ * Bridge function to be called by the git code to verify a signed payload\n+ */\n+int check_signature(const char *payload, size_t plen, const char *signature,\n+\tsize_t slen, struct signature *sigc);\n+\n+/*\n+ * Prints the results of either signing or verifying the payload in the\n+ * signatures struct. If the OUTPUT_VERBOSE flag is specified, then the\n+ * payload is printed to stdout. If the OUTPUT_RAW flag is specified, \n+ * the raw status output from the signing tool is printed to stderr, \n+ * otherwise, the nice results from the tool is printed to stderr.\n+ */\n+void print_signatures(const struct signatures *sigs, unsigned flags);\n+\n+/*\n+ * Bridge function to be called by the git code to print a signature\n+ */\n+void print_signature_buffer(const struct signature *sigc, unsigned flags);\n+\n+/*\n+ * Appends each of the detached signatures to the end of the strbuf\n+ * passed in. Returns the number of signatures appended to the buffer.\n+ */\n+size_t strbuf_append_signatures(struct strbuf *buf, const struct signatures *sigs);\n+\n+/*\n+ * Translate the name of the signature tool into the enumerated value\n+ * for the signature type.\n+ */\n+enum signature_type signature_type_by_name(const char *name);\n+const char *signature_type_name(enum signature_type st);\n+\n+/*\n+ * Config related functions\n+ */\n+int git_signing_config(const char *var, const char *value, void *cb);\n+void set_signing_key(const char *key, enum signature_type st);\n+const char *get_signing_key(enum signature_type st);\n+void set_signing_program(const char *program, enum signature_type st);\n+const char *get_signing_program(enum signature_type st);\n+void set_signature_type(enum signature_type st);\n+enum signature_type get_signature_type(void);\n+\n+#endif\n+\ndiff --git a/signing-tool-openpgp.c b/signing-tool-openpgp.c\nnew file mode 100644\nindex 000000000..93b63b36d\n--- /dev/null\n+++ b/signing-tool-openpgp.c\n@@ -0,0 +1,409 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+#include \"strbuf.h\"\n+#include \"signing-interface.h\"\n+#include \"signing-tool.h\"\n+#include \"sigchain.h\"\n+#include \"tempfile.h\"\n+\n+static int openpgp_sign(const char *payload, size_t size,\n+\t\tstruct signature **sig, const char *key);\n+static size_t openpgp_parse(const char *payload, size_t size,\n+\t\tstruct signature **sig);\n+static int openpgp_verify(const char *payload, size_t size,\n+\t\tstruct signature *sig);\n+static void openpgp_print(const struct signature *sig, unsigned flags);\n+static int openpgp_config(const char *, const char *, void *);\n+static void openpgp_set_key(const char *);\n+static const char *openpgp_get_key(void);\n+static void openpgp_set_program(const char *);\n+static const char *openpgp_get_program(void);\n+\n+const struct signing_tool openpgp_tool = {\n+\t.st = OPENPGP_SIGNATURE,\n+\t.name = \"openpgp\",\n+\t.sign = &openpgp_sign,\n+\t.parse = &openpgp_parse,\n+\t.verify = &openpgp_verify,\n+\t.print = &openpgp_print,\n+\t.config = &openpgp_config,\n+\t.set_key = &openpgp_set_key,\n+\t.get_key = &openpgp_get_key,\n+\t.set_program = &openpgp_set_program,\n+\t.get_program = &openpgp_get_program\n+};\n+\n+static const char *program = \"gpg\";\n+static const char *signing_key = NULL;\n+static const char *keyring = NULL;\n+static int no_default_keyring = 0;\n+struct regex_pattern {\n+\tconst char * begin;\n+\tconst char * end;\n+};\n+static struct regex_pattern patterns[2] = {\n+\t{ \"^-----BEGIN PGP SIGNATURE-----\\n\", \"-----END PGP SIGNATURE-----\\n\" },\n+\t{ \"^-----BEGIN PGP MESSAGE-----\\n\", \"-----END PGP MESSAGE-----\\n\" }\n+};\n+\n+static int openpgp_sign(const char *payload, size_t size,\n+\t\tstruct signature **sig, const char *key)\n+{\n+\tstruct child_process gpg = CHILD_PROCESS_INIT;\n+\tstruct signature *psig;\n+\tstruct strbuf *psignature, *pstatus;\n+\tint ret;\n+\tsize_t i, j;\n+\tconst char *skey = (!key || !*key) ? signing_key : key;\n+\n+\t/*\n+\t * Create the signature.\n+\t */\n+\tif (sig) {\n+\t\tpsig = *sig;\n+\t\tstrbuf_init(&(psig->sig), 0);\n+\t\tstrbuf_init(&(psig->output), 0);\n+\t\tstrbuf_init(&(psig->status), 0);\n+\t\tpsig->st = OPENPGP_SIGNATURE;\n+\t\tpsig->result = 0;\n+\t\tpsig->signer = NULL;\n+\t\tpsig->key = NULL;\n+\t\tpsignature = &(psig->sig);\n+\t\tpstatus = &(psig->status);\n+\t} else {\n+\t\tpsignature = NULL;\n+\t\tpstatus = NULL;\n+\t}\n+\n+\targv_array_pushl(&gpg.args,\n+\t\t\tprogram,\n+\t\t\t\"--status-fd=2\",\n+\t\t\t\"-bsau\", skey,\n+\t\t\tNULL);\n+\n+\t/*\n+\t * When the username signingkey is bad, program could be terminated\n+\t * because gpg exits without reading and then write gets SIGPIPE.\n+\t */\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\tret = pipe_command(&gpg, payload, size,\n+\t\t\tpsignature, 1024, pstatus, 0);\n+\tsigchain_pop(SIGPIPE);\n+\n+\tif (!sig)\n+\t\treturn !!ret;\n+\n+\t/* Check for success status from gpg */\n+\tret |= !strstr(pstatus->buf, \"\\n[GNUPG:] SIG_CREATED \");\n+\n+\tif (ret)\n+\t\treturn error(_(\"gpg failed to sign the data\"));\n+\n+\t/* Mark the signature as good */\n+\tpsig->result = 'G';\n+\n+\t/* Strip CR from the line endings, in case we are on Windows. */\n+\tfor (i = j = 0; i < psig->sig.len; i++)\n+\t\tif (psig->sig.buf[i] != '\\r') {\n+\t\t\tif (i != j)\n+\t\t\t\tpsig->sig.buf[j] = psig->sig.buf[i];\n+\t\t\tj++;\n+\t\t}\n+\tstrbuf_setlen(&(psig->sig), j);\n+\n+\t/* Store the key we used */\n+\tpsig->key = xstrdup(skey);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * To get all OpenPGP signatures in a payload, repeatedly call this function\n+ * giving it the remainder of the payload as the payload pointer. The return\n+ * value is the index of the first char of the signature in the payload. If\n+ * no signature is found, size is returned.\n+ */\n+static size_t openpgp_parse(const char *payload, size_t size,\n+\t\tstruct signature **sig)\n+{\n+\tint i, ret;\n+\tregex_t rbegin;\n+\tregex_t rend;\n+\tregmatch_t bmatch;\n+\tregmatch_t ematch;\n+\tsize_t begin, end;\n+\tstruct signature *psig;\n+\tstatic char errbuf[1024];\n+\n+\tif (size == 0)\n+\t\treturn size;\n+\n+\t/*\n+\t * Figure out if any OpenPGP signatures are in the payload and which\n+\t * begin pattern matches the first signature in the payload.\n+\t */\n+\tfor (i = 0; i < ARRAY_SIZE(patterns); i++) {\n+\t\tif ((ret = regcomp(&rbegin, patterns[i].begin, REG_EXTENDED|REG_NEWLINE))) {\n+\t\t\tregerror(ret, &rbegin, errbuf, 1024);\n+\t\t\tBUG(\"Failed to compile regex: %s\\n\", errbuf);\n+\n+\t\t\treturn size;\n+\t\t}\n+\t\tif ((ret = regcomp(&rend, patterns[i].end, REG_EXTENDED|REG_NEWLINE))) {\n+\t\t\tregerror(ret, &rend, errbuf, 1024);\n+\t\t\tBUG(\"Failed to compile regex: %s\\n\", errbuf);\n+\n+\t\t\treturn size;\n+\t\t}\n+\n+\t\tbegin = end = 0;\n+\t\tif (regexec(&rbegin, payload, 1, &bmatch, 0) ||\n+\t\t\tregexec(&rend, payload, 1, &ematch, 0)) {\n+\t\t\tbegin = size;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tbegin = bmatch.rm_so;\n+\t\tend = ematch.rm_eo;\n+\n+\t\tbreak;\n+\t}\n+\tif (begin == size)\n+\t\tgoto next;\n+\n+\t/*\n+\t * Create the signature.\n+\t */\n+\tif (sig) {\n+\t\tpsig = *sig;\n+\t\tpsig = xmalloc(sizeof(struct signature));\n+\t\tstrbuf_init(&(psig->sig), end - begin);\n+\t\tstrbuf_add(&(psig->sig), payload + begin, end - begin);\n+\t\tstrbuf_init(&(psig->output), 0);\n+\t\tstrbuf_init(&(psig->status), 0);\n+\t\tpsig->st = OPENPGP_SIGNATURE;\n+\t\tpsig->result = 0;\n+\t\tpsig->signer = NULL;\n+\t\tpsig->key = NULL;\n+\t}\n+\tnext:\n+\t\tregfree(&rbegin);\n+\t\tregfree(&rend);\n+\n+\treturn begin;\n+}\n+\n+/* An exclusive status -- only one of them can appear in output */\n+#define GPG_STATUS_EXCLUSIVE\t(1<<0)\n+/* The status includes key identifier */\n+#define GPG_STATUS_KEYID\t(1<<1)\n+/* The status includes user identifier */\n+#define GPG_STATUS_UID\t\t(1<<2)\n+/* The status includes key fingerprints */\n+#define GPG_STATUS_FINGERPRINT\t(1<<3)\n+\n+/* Short-hand for standard exclusive *SIG status with keyid & UID */\n+#define GPG_STATUS_STDSIG\t(GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)\n+\n+static struct {\n+\tchar result;\n+\tconst char *check;\n+\tunsigned int flags;\n+} sigcheck_gpg_status[] = {\n+\t{ 'G', \"GOODSIG \", GPG_STATUS_STDSIG },\n+\t{ 'B', \"BADSIG \", GPG_STATUS_STDSIG },\n+\t{ 'U', \"TRUST_NEVER\", 0 },\n+\t{ 'U', \"TRUST_UNDEFINED\", 0 },\n+\t{ 'E', \"ERRSIG \", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },\n+\t{ 'X', \"EXPSIG \", GPG_STATUS_STDSIG },\n+\t{ 'Y', \"EXPKEYSIG \", GPG_STATUS_STDSIG },\n+\t{ 'R', \"REVKEYSIG \", GPG_STATUS_STDSIG },\n+\t{ 0, \"VALIDSIG \", GPG_STATUS_FINGERPRINT },\n+};\n+\n+static void parse_output(struct signature *sigc)\n+{\n+\tconst char *buf = sigc->status.buf;\n+\tconst char *line, *next;\n+\tint i, j;\n+\tint seen_exclusive_status = 0;\n+\n+\t/* Iterate over all lines */\n+\tfor (line = buf; *line; line = strchrnul(line+1, '\\n')) {\n+\t\twhile (*line == '\\n')\n+\t\t\tline++;\n+\t\t/* Skip lines that don't start with GNUPG status */\n+\t\tif (!skip_prefix(line, \"[GNUPG:] \", &line))\n+\t\t\tcontinue;\n+\n+\t\t/* Iterate over all search strings */\n+\t\tfor (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {\n+\t\t\tif (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {\n+\t\t\t\tif (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {\n+\t\t\t\t\tif (seen_exclusive_status++)\n+\t\t\t\t\t\tgoto found_duplicate_status;\n+\t\t\t\t}\n+\n+\t\t\t\tif (sigcheck_gpg_status[i].result)\n+\t\t\t\t\tsigc->result = sigcheck_gpg_status[i].result;\n+\t\t\t\t/* Do we have key information? */\n+\t\t\t\tif (sigcheck_gpg_status[i].flags & GPG_STATUS_KEYID) {\n+\t\t\t\t\tnext = strchrnul(line, ' ');\n+\t\t\t\t\tfree(sigc->key);\n+\t\t\t\t\tsigc->key = xmemdupz(line, next - line);\n+\t\t\t\t\t/* Do we have signer information? */\n+\t\t\t\t\tif (*next && (sigcheck_gpg_status[i].flags & GPG_STATUS_UID)) {\n+\t\t\t\t\t\tline = next + 1;\n+\t\t\t\t\t\tnext = strchrnul(line, '\\n');\n+\t\t\t\t\t\tfree(sigc->signer);\n+\t\t\t\t\t\tsigc->signer = xmemdupz(line, next - line);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\t/* Do we have fingerprint? */\n+\t\t\t\tif (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {\n+\t\t\t\t\tnext = strchrnul(line, ' ');\n+\t\t\t\t\tfree(sigc->fingerprint);\n+\t\t\t\t\tsigc->fingerprint = xmemdupz(line, next - line);\n+\n+\t\t\t\t\t/* Skip interim fields */\n+\t\t\t\t\tfor (j = 9; j > 0; j--) {\n+\t\t\t\t\t\tif (!*next)\n+\t\t\t\t\t\t\tbreak;\n+\t\t\t\t\t\tline = next + 1;\n+\t\t\t\t\t\tnext = strchrnul(line, ' ');\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tnext = strchrnul(line, '\\n');\n+\t\t\t\t\tfree(sigc->primary_key_fingerprint);\n+\t\t\t\t\tsigc->primary_key_fingerprint = xmemdupz(line, next - line);\n+\t\t\t\t}\n+\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn;\n+\n+found_duplicate_status:\n+\t/*\n+\t * GOODSIG, BADSIG etc. can occur only once for each signature.\n+\t * Therefore, if we had more than one then we're dealing with multiple\n+\t * signatures.  We don't support them currently, and they're rather\n+\t * hard to create, so something is likely fishy and we should reject\n+\t * them altogether.\n+\t */\n+\tsigc->result = 'E';\n+\t/* Clear partial data to avoid confusion */\n+\tFREE_AND_NULL(sigc->primary_key_fingerprint);\n+\tFREE_AND_NULL(sigc->fingerprint);\n+\tFREE_AND_NULL(sigc->signer);\n+\tFREE_AND_NULL(sigc->key);\n+}\n+\n+static int openpgp_verify(const char *payload, size_t size,\n+\t\tstruct signature *sig)\n+{\n+\tstruct child_process gpg = CHILD_PROCESS_INIT;\n+\tstruct tempfile *temp;\n+\tint ret;\n+\n+\ttemp = mks_tempfile_t(\".git_vtag_tmpXXXXXX\");\n+\tif (!temp)\n+\t\treturn error_errno(_(\"could not create temporary file\"));\n+\tif (write_in_full(temp->fd, sig->sig.buf, sig->sig.len) < 0 ||\n+\t    close_tempfile_gently(temp) < 0) {\n+\t\terror_errno(_(\"failed writing detached signature to '%s'\"),\n+\t\t\t\ttemp->filename.buf);\n+\t\tdelete_tempfile(&temp);\n+\t\treturn -1;\n+\t}\n+\n+\targv_array_push(&gpg.args, program);\n+\tif (keyring)\n+\t\targv_array_pushl(&gpg.args, \"--keyring\", keyring, NULL);\n+\tif (no_default_keyring)\n+\t\targv_array_push(&gpg.args, \"--no-default-keyring\");\n+\targv_array_pushl(&gpg.args,\n+\t\t\t\"--keyid-format=long\",\n+\t\t\t\"--status-fd=1\",\n+\t\t\t\"--verify\", temp->filename.buf, \"-\",\n+\t\t\tNULL);\n+\n+\tstrbuf_reset(&(sig->status));\n+\tstrbuf_reset(&(sig->output));\n+\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\tret = pipe_command(&gpg, payload, size,\n+\t\t\t&(sig->status), 0, &(sig->output), 0);\n+\tsigchain_pop(SIGPIPE);\n+\n+\tdelete_tempfile(&temp);\n+\n+\tret |= !strstr(sig->status.buf, \"\\n[GNUPG:] GOODSIG \");\n+\n+\tif (ret && !sig->output.len)\n+\t\treturn !!ret;\n+\n+\tparse_output(sig);\n+\n+\tret |= sig->result != 'G' && sig->result != 'U';\n+\n+\treturn !!ret;\n+}\n+\n+static void openpgp_print(const struct signature *sig, unsigned flags)\n+{\n+\tconst char *output = flags & OUTPUT_RAW ?\n+\t\tsig->status.buf : sig->output.buf;\n+\n+\tif (flags & OUTPUT_VERBOSE && sig->sig.buf)\n+\t\tfputs(sig->sig.buf, stdout);\n+\n+\tif (output)\n+\t\tfputs(output, stderr);\n+}\n+\n+static int openpgp_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"program\"))\n+\t\treturn git_config_string(&program, var, value);\n+\n+\tif (!strcmp(var, \"key\"))\n+\t\treturn git_config_string(&signing_key, var, value);\n+\n+\tif (!strcmp(var, \"keyring\"))\n+\t\treturn git_config_string(&keyring, var, value);\n+\n+\tif (!strcmp(var, \"nodefaultkeyring\")) {\n+\t\tno_default_keyring = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn 0;\n+}\n+\n+static void openpgp_set_key(const char *key)\n+{\n+\tfree((void*)signing_key);\n+\tsigning_key = xstrdup(key);\n+}\n+\n+static const char *openpgp_get_key(void)\n+{\n+\tif (signing_key)\n+\t\treturn signing_key;\n+\treturn git_committer_info(IDENT_STRICT|IDENT_NO_DATE);\n+}\n+\n+static void openpgp_set_program(const char *signing_program)\n+{\n+\tfree((void*)program);\n+\tprogram = xstrdup(signing_program);\n+}\n+\n+\n+static const char *openpgp_get_program(void)\n+{\n+\tif (program)\n+\t\treturn program;\n+\treturn git_committer_info(IDENT_STRICT|IDENT_NO_DATE);\n+}\n\\ No newline at end of file\ndiff --git a/signing-tool-x509.c b/signing-tool-x509.c\nnew file mode 100644\nindex 000000000..b7de56924\n--- /dev/null\n+++ b/signing-tool-x509.c\n@@ -0,0 +1,383 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"run-command.h\"\n+#include \"strbuf.h\"\n+#include \"signing-interface.h\"\n+#include \"signing-tool.h\"\n+#include \"sigchain.h\"\n+#include \"tempfile.h\"\n+\n+static int x509_sign(const char *payload, size_t size,\n+\t\tstruct signature **sig, const char *key);\n+static size_t x509_parse(const char *payload, size_t size,\n+\t\tstruct signature **sig);\n+static int x509_verify(const char *payload, size_t size,\n+\t\tstruct signature *sig);\n+static void x509_print(const struct signature *sig, unsigned flags);\n+static int x509_config(const char *, const char *, void *);\n+static void x509_set_key(const char *);\n+static const char *x509_get_key(void);\n+static void x509_set_program(const char *);\n+static const char *x509_get_program(void);\n+\n+const struct signing_tool x509_tool = {\n+\t.st = X509_SIGNATURE,\n+\t.name = \"x509\",\n+\t.sign = &x509_sign,\n+\t.parse = &x509_parse,\n+\t.verify = &x509_verify,\n+\t.print = &x509_print,\n+\t.config = &x509_config,\n+\t.set_key = &x509_set_key,\n+\t.get_key = &x509_get_key,\n+\t.set_program = &x509_set_program,\n+\t.get_program = &x509_get_program\n+};\n+\n+static const char *program = \"gpgsm\";\n+static const char *signing_key = NULL;\n+struct regex_pattern {\n+\tconst char * begin;\n+\tconst char * end;\n+};\n+static struct regex_pattern pattern = {\n+\t\"^-----BEGIN SIGNED MESSAGE-----\\n\",\n+\t\"^-----END SIGNED MESSAGE-----\\n\"\n+};\n+\n+static int x509_sign(const char *payload, size_t size,\n+\t\tstruct signature **sig, const char *key)\n+{\n+\tstruct child_process gpgsm = CHILD_PROCESS_INIT;\n+\tstruct signature *psig;\n+\tstruct strbuf *psignature, *pstatus;\n+\tint ret;\n+\tsize_t i, j;\n+\tconst char *skey = (!key || !*key) ? signing_key : key;\n+\n+\t/*\n+\t * Create the signature.\n+\t */\n+\tif (sig) {\n+\t\tpsig = *sig;\n+\t\tstrbuf_init(&(psig->sig), 0);\n+\t\tstrbuf_init(&(psig->output), 0);\n+\t\tstrbuf_init(&(psig->status), 0);\n+\t\tpsig->st = X509_SIGNATURE;\n+\t\tpsig->result = 0;\n+\t\tpsig->signer = NULL;\n+\t\tpsig->key = NULL;\n+\t\tpsignature = &(psig->sig);\n+\t\tpstatus = &(psig->status);\n+\t} else {\n+\t\tpsignature = NULL;\n+\t\tpstatus = NULL;\n+\t}\n+\n+\targv_array_pushl(&gpgsm.args,\n+\t\t\tprogram,\n+\t\t\t\"--status-fd=2\",\n+\t\t\t\"-bsau\", skey,\n+\t\t\tNULL);\n+\n+\t/*\n+\t * When the username signingkey is bad, program could be terminated\n+\t * because gpgsm exits without reading and then write gets SIGPIPE.\n+\t */\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\tret = pipe_command(&gpgsm, payload, size,\n+\t\t\tpsignature, 1024, pstatus, 0);\n+\tsigchain_pop(SIGPIPE);\n+\n+\tif (!sig)\n+\t\treturn !!ret;\n+\n+\tret |= !strstr(pstatus->buf, \"\\n[GNUPG:] SIG_CREATED \");\n+\tif (ret)\n+\t\treturn error(_(\"gpgsm failed to sign the data\"));\n+\n+\t/* Mark the signature as good. */\n+\tpsig->result = 'G';\n+\n+\t/* Strip CR from the line endings, in case we are on Windows. */\n+\tfor (i = j = 0; i < psig->sig.len; i++)\n+\t\tif (psig->sig.buf[i] != '\\r') {\n+\t\t\tif (i != j)\n+\t\t\t\tpsig->sig.buf[j] = psig->sig.buf[i];\n+\t\t\tj++;\n+\t\t}\n+\tstrbuf_setlen(&(psig->sig), j);\n+\n+\t/* Store the key we used */\n+\tpsig->key = xstrdup(skey);\n+\n+\treturn 0;\n+}\n+\n+static size_t x509_parse(const char *payload, size_t size,\n+\t\tstruct signature **sig)\n+{\n+\tint ret;\n+\tregex_t rbegin;\n+\tregex_t rend;\n+\tregmatch_t bmatch;\n+\tregmatch_t ematch;\n+\tsize_t begin, end;\n+\tstruct signature *psig;\n+\tstatic char errbuf[1024];\n+\n+\tif (size == 0)\n+\t\treturn size;\n+\n+\t/*\n+\t * Find the first x509 signature in the payload and copy it into the\n+\t * signature struct.\n+\t */\n+\tif ((ret = regcomp(&rbegin, pattern.begin, REG_EXTENDED|REG_NEWLINE))) {\n+\t\tregerror(ret, &rbegin, errbuf, 1024);\n+\t\tBUG(\"Failed to compile regex: %s\\n\", errbuf);\n+\n+\t\treturn size;\n+\t}\n+\tif ((ret = regcomp(&rend, pattern.end, REG_EXTENDED|REG_NEWLINE))) {\n+\t\tregerror(ret, &rend, errbuf, 1024);\n+\t\tBUG(\"Failed to compile regex: %s\\n\", errbuf);\n+\n+\t\treturn size;\n+\t}\n+\n+\tbegin = end = 0;\n+\tif (regexec(&rbegin, payload, 1, &bmatch, 0) ||\n+\t\tregexec(&rend, payload, 1, &ematch, 0)) {\n+\t\tbegin = size;\n+\t}\n+\tif (begin == size)\n+\t\tgoto next;\n+\n+\tbegin = bmatch.rm_so;\n+\tend = ematch.rm_eo;\n+\n+\t/*\n+\t * Create the signature.\n+\t */\n+\tif (sig) {\n+\t\tpsig = *sig;\n+\t\tpsig = xmalloc(sizeof(struct signature));\n+\t\tstrbuf_init(&(psig->sig), end - begin);\n+\t\tstrbuf_add(&(psig->sig), payload + begin, end - begin);\n+\t\tstrbuf_init(&(psig->output), 0);\n+\t\tstrbuf_init(&(psig->status), 0);\n+\t\tpsig->st = X509_SIGNATURE;\n+\t\tpsig->result = 0;\n+\t\tpsig->signer = NULL;\n+\t\tpsig->key = NULL;\n+\t}\n+\n+\tnext:\n+\t\tregfree(&rbegin);\n+\t\tregfree(&rend);\n+\n+\treturn begin;\n+}\n+\n+/* An exclusive status -- only one of them can appear in output */\n+#define GPG_STATUS_EXCLUSIVE\t(1<<0)\n+/* The status includes key identifier */\n+#define GPG_STATUS_KEYID\t(1<<1)\n+/* The status includes user identifier */\n+#define GPG_STATUS_UID\t\t(1<<2)\n+/* The status includes key fingerprints */\n+#define GPG_STATUS_FINGERPRINT\t(1<<3)\n+\n+/* Short-hand for standard exclusive *SIG status with keyid & UID */\n+#define GPG_STATUS_STDSIG\t(GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)\n+\n+static struct {\n+\tchar result;\n+\tconst char *check;\n+\tunsigned int flags;\n+} sigcheck_gpg_status[] = {\n+\t{ 'G', \"GOODSIG \", GPG_STATUS_STDSIG },\n+\t{ 'B', \"BADSIG \", GPG_STATUS_STDSIG },\n+\t{ 'U', \"TRUST_NEVER\", 0 },\n+\t{ 'U', \"TRUST_UNDEFINED\", 0 },\n+\t{ 'E', \"ERRSIG \", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },\n+\t{ 'X', \"EXPSIG \", GPG_STATUS_STDSIG },\n+\t{ 'Y', \"EXPKEYSIG \", GPG_STATUS_STDSIG },\n+\t{ 'R', \"REVKEYSIG \", GPG_STATUS_STDSIG },\n+\t{ 0, \"VALIDSIG \", GPG_STATUS_FINGERPRINT },\n+};\n+\n+static void parse_output(struct signature *sigc)\n+{\n+\tconst char *buf = sigc->status.buf;\n+\tconst char *line, *next;\n+\tint i, j;\n+\tint seen_exclusive_status = 0;\n+\n+\t/* Iterate over all lines */\n+\tfor (line = buf; *line; line = strchrnul(line+1, '\\n')) {\n+\t\twhile (*line == '\\n')\n+\t\t\tline++;\n+\t\t/* Skip lines that don't start with GNUPG status */\n+\t\tif (!skip_prefix(line, \"[GNUPG:] \", &line))\n+\t\t\tcontinue;\n+\n+\t\t/* Iterate over all search strings */\n+\t\tfor (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {\n+\t\t\tif (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {\n+\t\t\t\tif (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {\n+\t\t\t\t\tif (seen_exclusive_status++)\n+\t\t\t\t\t\tgoto found_duplicate_status;\n+\t\t\t\t}\n+\n+\t\t\t\tif (sigcheck_gpg_status[i].result)\n+\t\t\t\t\tsigc->result = sigcheck_gpg_status[i].result;\n+\t\t\t\t/* Do we have key information? */\n+\t\t\t\tif (sigcheck_gpg_status[i].flags & GPG_STATUS_KEYID) {\n+\t\t\t\t\tnext = strchrnul(line, ' ');\n+\t\t\t\t\tfree(sigc->key);\n+\t\t\t\t\tsigc->key = xmemdupz(line, next - line);\n+\t\t\t\t\t/* Do we have signer information? */\n+\t\t\t\t\tif (*next && (sigcheck_gpg_status[i].flags & GPG_STATUS_UID)) {\n+\t\t\t\t\t\tline = next + 1;\n+\t\t\t\t\t\tnext = strchrnul(line, '\\n');\n+\t\t\t\t\t\tfree(sigc->signer);\n+\t\t\t\t\t\tsigc->signer = xmemdupz(line, next - line);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\t/* Do we have fingerprint? */\n+\t\t\t\tif (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {\n+\t\t\t\t\tnext = strchrnul(line, ' ');\n+\t\t\t\t\tfree(sigc->fingerprint);\n+\t\t\t\t\tsigc->fingerprint = xmemdupz(line, next - line);\n+\n+\t\t\t\t\t/* Skip interim fields */\n+\t\t\t\t\tfor (j = 9; j > 0; j--) {\n+\t\t\t\t\t\tif (!*next)\n+\t\t\t\t\t\t\tbreak;\n+\t\t\t\t\t\tline = next + 1;\n+\t\t\t\t\t\tnext = strchrnul(line, ' ');\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tnext = strchrnul(line, '\\n');\n+\t\t\t\t\tfree(sigc->primary_key_fingerprint);\n+\t\t\t\t\tsigc->primary_key_fingerprint = xmemdupz(line, next - line);\n+\t\t\t\t}\n+\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn;\n+\n+found_duplicate_status:\n+\t/*\n+\t * GOODSIG, BADSIG etc. can occur only once for each signature.\n+\t * Therefore, if we had more than one then we're dealing with multiple\n+\t * signatures.  We don't support them currently, and they're rather\n+\t * hard to create, so something is likely fishy and we should reject\n+\t * them altogether.\n+\t */\n+\tsigc->result = 'E';\n+\t/* Clear partial data to avoid confusion */\n+\tFREE_AND_NULL(sigc->primary_key_fingerprint);\n+\tFREE_AND_NULL(sigc->fingerprint);\n+\tFREE_AND_NULL(sigc->signer);\n+\tFREE_AND_NULL(sigc->key);\n+}\n+\n+static int x509_verify(const char *payload, size_t size,\n+\t\tstruct signature *sig)\n+{\n+\tstruct child_process gpgsm = CHILD_PROCESS_INIT;\n+\tstruct tempfile *temp;\n+\tint ret;\n+\n+\ttemp = mks_tempfile_t(\".git_vtag_tmpXXXXXX\");\n+\tif (!temp)\n+\t\treturn error_errno(_(\"could not create temporary file\"));\n+\tif (write_in_full(temp->fd, sig->sig.buf, sig->sig.len) < 0 ||\n+\t    close_tempfile_gently(temp) < 0) {\n+\t\terror_errno(_(\"failed writing detached signature to '%s'\"),\n+\t\t\t\ttemp->filename.buf);\n+\t\tdelete_tempfile(&temp);\n+\t\treturn -1;\n+\t}\n+\n+\targv_array_push(&gpgsm.args, program);\n+\targv_array_pushl(&gpgsm.args,\n+\t\t\t\"--status-fd=1\",\n+\t\t\t\"--verify\", temp->filename.buf, \"-\",\n+\t\t\tNULL);\n+\n+\tstrbuf_reset(&(sig->status));\n+\tstrbuf_reset(&(sig->output));\n+\n+\tsigchain_push(SIGPIPE, SIG_IGN);\n+\tret = pipe_command(&gpgsm, payload, size,\n+\t\t\t&(sig->status), 0, &(sig->output), 0);\n+\tsigchain_pop(SIGPIPE);\n+\n+\tdelete_tempfile(&temp);\n+\n+\tret |= !strstr(sig->status.buf, \"\\n[GNUPG:] GOODSIG \");\n+\n+\tif (ret && !sig->output.len)\n+\t\treturn !!ret;\n+\n+\tparse_output(sig);\n+\n+\tret |= sig->result != 'G' && sig->result != 'U';\n+\n+\treturn !!ret;\n+}\n+\n+static void x509_print(const struct signature *sig, unsigned flags)\n+{\n+\tconst char *output = flags & OUTPUT_RAW ?\n+\t\tsig->status.buf : sig->output.buf;\n+\n+\tif (flags & OUTPUT_VERBOSE && sig->sig.buf)\n+\t\tfputs(sig->sig.buf, stdout);\n+\n+\tif (output)\n+\t\tfputs(output, stderr);\n+}\n+\n+static int x509_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcmp(var, \"program\"))\n+\t\treturn git_config_string(&program, var, value);\n+\n+\tif (!strcmp(var, \"key\"))\n+\t\treturn git_config_string(&signing_key, var, value);\n+\n+\treturn 0;\n+}\n+\n+static void x509_set_key(const char *key)\n+{\n+\tfree((void*)signing_key);\n+\tsigning_key = xstrdup(key);\n+}\n+\n+static const char *x509_get_key(void)\n+{\n+\tif (signing_key)\n+\t\treturn signing_key;\n+\treturn git_committer_info(IDENT_STRICT|IDENT_NO_DATE);\n+}\n+\n+static void x509_set_program(const char *signing_program)\n+{\n+\tfree((void*)program);\n+\tprogram = xstrdup(signing_program);\n+}\n+\n+static const char *x509_get_program(void)\n+{\n+\tif (program)\n+\t\treturn program;\n+\treturn git_committer_info(IDENT_STRICT|IDENT_NO_DATE);\n+}\n\\ No newline at end of file\ndiff --git a/signing-tool.h b/signing-tool.h\nnew file mode 100644\nindex 000000000..ee7ccc7a5\n--- /dev/null\n+++ b/signing-tool.h\n@@ -0,0 +1,35 @@\n+#ifndef SIGNING_TOOL_H\n+#define SIGNING_TOOL_H\n+\n+struct strbuf;\n+struct signature;\n+\n+typedef int (*sign_fn)(const char *payload, size_t size,\n+\tstruct signature **sig, const char *key);\n+typedef size_t (*parse_fn)(const char *payload, size_t size,\n+\tstruct signature **sig);\n+typedef int (*verify_fn)(const char *payload, size_t size,\n+\tstruct signature *sig);\n+typedef void (*print_fn)(const struct signature *sig, unsigned flags);\n+typedef int (*config_fn)(const char *var, const char *value, void *cb);\n+typedef void (*set_key_fn)(const char *key);\n+typedef const char *(*get_key_fn)(void);\n+typedef void (*set_program_fn)(const char *signing_program);\n+typedef const char *(*get_program_fn)(void);\n+\n+struct signing_tool {\n+\tconst enum signature_type st;\n+\tconst char* name;\n+\tsign_fn sign;\n+\tparse_fn parse;\n+\tverify_fn verify;\n+\tprint_fn print;\n+\tconfig_fn config;\n+\tset_key_fn set_key;\n+\tget_key_fn get_key;\n+\tset_program_fn set_program;\n+\tget_program_fn get_program;\n+};\n+\n+#endif\n+\n-- \n2.11.0\n\n\n"}]}