git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 2/5] Signing API: Added new signing interface API

From
IEIbrahim El <ibrahim.el@pm.me>
Date
Aug 26, 2019, 19:58 UTC
Message-ID
<CkxnpIEi2c1o1FmI2ZJP8CJQybGMZFbL6uQAd3w7ApKdaki4vcCrlgbTWmPMrrfIedBSvLV56q6txzvCo7bnO1wHlS6cB3bUYpYfNW80w_s=@pm.me>
From: Ibrahim El Rhezzali <ibrahim.el@pm.me>
7e3e6c9e4 Added new signing interface API
Adding files for the new signing interface and also support drivers for the two existing GPG and GPGSM X.509 tools
Signed-off-by: Ibrahim El <ibrahim.el@pm.me>
---
 Makefile               |   3 +
 signing-interface.c    | 487 +++++++++++++++++++++++++++++++++++++++++++++++++
 signing-interface.h    | 151 +++++++++++++++
 signing-tool-openpgp.c | 409 +++++++++++++++++++++++++++++++++++++++++
 signing-tool-x509.c    | 383 ++++++++++++++++++++++++++++++++++++++
 signing-tool.h         |  35 ++++
 6 files changed, 1468 insertions(+)
 create mode 100644 signing-interface.c
 create mode 100644 signing-interface.h
 create mode 100644 signing-tool-openpgp.c
 create mode 100644 signing-tool-x509.c
 create mode 100644 signing-tool.h
diff --git a/Makefile b/Makefile
index f58bf14c7..244540e8d 100644
--- a/Makefile
+++ b/Makefile
@@ -978,6 +978,9 @@ LIB_OBJS += sha1-name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
 LIB_OBJS += sigchain.o
+LIB_OBJS += signing-interface.o
+LIB_OBJS += signing-tool-openpgp.o
+LIB_OBJS += signing-tool-x509.o
 LIB_OBJS += split-index.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
diff --git a/signing-interface.c b/signing-interface.c
new file mode 100644
index 000000000..c744ef499
--- /dev/null
+++ b/signing-interface.c
@@ -0,0 +1,487 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include "cache.h"
+#include "config.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "signing-interface.h"
+#include "signing-tool.h"
+#include "sigchain.h"
+#include "tempfile.h"
+
+extern const struct signing_tool openpgp_tool;
+extern const struct signing_tool x509_tool;
+
+static const struct signing_tool *signing_tools[SIGNATURE_TYPE_COUNT] = {
+	&openpgp_tool,
+	&x509_tool,
+};
+
+enum signature_type default_type = SIGNATURE_TYPE_DEFAULT;
+static const char* unknown_signature_type = "unknown signature type";
+static char* default_signing_key = NULL;
+
+static void add_signature(struct signatures *sigs, struct signature *sig) {
+	if (!sigs || !sig)
+		return;
+	ALLOC_GROW(sigs->sigs, sigs->nsigs + 1, sigs->alloc);
+	sigs->sigs[sigs->nsigs++] = sig;
+}
+
+void signatures_clear(struct signatures *sigs)
+{
+	size_t i;
+	struct signature *psig;
+
+	if (!sigs) return;
+	
+	for (i = 0; i < sigs->nsigs; i++) {
+		psig = sigs->sigs[i];
+		strbuf_release(&(psig->sig));
+		strbuf_release(&(psig->output));
+		strbuf_release(&(psig->status));
+		FREE_AND_NULL(psig->signer);
+		FREE_AND_NULL(psig->key);
+		FREE_AND_NULL(psig->fingerprint);
+		FREE_AND_NULL(psig->key);
+		FREE_AND_NULL(psig);
+	}
+	FREE_AND_NULL(sigs->sigs);
+	sigs->nsigs = 0;
+	sigs->alloc = 0;
+}
+
+void signature_clear(struct signature *sigc)
+{
+	FREE_AND_NULL(sigc->sig.buf);
+	FREE_AND_NULL(sigc->output.buf);
+	FREE_AND_NULL(sigc->status.buf);
+	FREE_AND_NULL(sigc->signer);
+	FREE_AND_NULL(sigc->key);
+	FREE_AND_NULL(sigc->fingerprint);
+	FREE_AND_NULL(sigc->primary_key_fingerprint);
+}
+
+int sign_payload(const char *payload, size_t size, struct signatures *sigs,
+		enum signature_type st, const char *signing_key)
+{
+	const struct signing_tool *tool;
+	struct signature *psig = xmalloc(sizeof(struct signature));
+	int ret;
+
+	fflush(stdout);
+
+	if (!sigs)
+		return error("invalid signatures passed to sign function");
+
+	if (!VALID_SIGNATURE_TYPE(st))
+		return error("unsupported signature type: %d", st);
+
+	tool = signing_tools[st];
+
+	if (!tool || !tool->sign)
+		BUG("signing tool %s undefined", signature_type_name(st));
+
+	ret = tool->sign(payload, size, &psig, signing_key);
+	if (!ret)
+		add_signature(sigs, psig);
+	else
+
+		return error("signing operation failed");
+
+	return 0;
+}
+
+int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
+{
+	struct signatures sigs = SIGNATURES_INIT;
+	enum signature_type st = default_type;
+
+	int ret = sign_payload(buffer->buf, buffer->len, &sigs, st, signing_key);
+
+	if (!ret)
+	{
+		strbuf_addstr(signature, sigs.sigs[0]->sig.buf);
+	}
+
+	return ret;
+}
+
+size_t parse_signatures(const char *payload, size_t size, 
+		struct signatures *sigs)
+{
+	enum signature_type st;
+	size_t first;
+	size_t begin = 0;
+	const struct signing_tool *tool;
+	struct signature *psig = NULL;
+
+	first = size;
+	for (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++) {
+		tool = signing_tools[st];
+
+		if (!tool || !tool->parse)
+			BUG("signing tool %s undefined", signature_type_name(st));
+
+		begin = tool->parse(payload, size, &psig);
+		if (begin < size) {
+			if (sigs)
+				add_signature(sigs, psig);
+			else
+				FREE_AND_NULL(psig);
+
+			first = begin;
+			continue;
+		}
+	}
+
+	return first;
+}
+
+size_t parse_signature(const char *buf, size_t size)
+{
+	size_t match;
+	struct signatures sigs = SIGNATURES_INIT;
+
+	if ( !buf || !size )
+		return size;
+
+	match = parse_signatures(buf, size, &sigs);
+
+	return match;
+}
+
+int verify_buffer_signatures(const char *payload, size_t size,
+		struct signatures *sigs)
+{
+	int ret = 0;
+	size_t i;
+	const struct signing_tool *tool;
+	struct signature *psig;
+
+	if (!sigs)
+		error("invalid signatures passed to verify function");
+
+	for (i = 0; i < sigs->nsigs; i++) {
+		psig = sigs->sigs[i];
+		tool = signing_tools[psig->st];
+
+		if (!tool || !tool->verify)
+			BUG("signing tool %s undefined", signature_type_name(psig->st));
+
+		ret |= tool->verify(payload, size, psig);
+	}
+
+	return ret;
+}
+
+int verify_signed_buffer(const char *payload, size_t payload_size,
+			 const char *signature, size_t signature_size,
+			 struct strbuf *output, struct strbuf *status)
+{
+	int ret;
+	enum signature_type st;
+	struct signature sig = SIGNATURE_INIT;
+	struct signatures sigs = SIGNATURES_INIT;
+
+	if ( !payload || !signature )
+		return error("invalid payload or signature sent !");
+
+	strbuf_addstr(&(sig.sig), signature);
+	add_signature(&sigs, &sig);
+
+	ret = verify_buffer_signatures(payload, payload_size, &sigs);
+
+	/*  Some how gpg.format is not sometimes applied, temporary fix to loop and STs */
+	if (ret)
+	{
+		for (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++)
+		{
+			sig.st = st;
+			ret = verify_buffer_signatures(payload, payload_size, &sigs);
+			if (!ret || sig.result != '0')
+				break;
+		}
+	}
+
+	if (output)
+		strbuf_addstr(output, sig.output.buf);
+	if (status)
+		strbuf_addstr(status, sig.status.buf);
+
+	return ret;
+}
+
+int check_signature(const char *payload, size_t plen, const char *signature,
+	size_t slen, struct signature *sigc)
+{
+	int status;
+	enum signature_type st;
+	struct signatures sigs = SIGNATURES_INIT;
+	struct signature sig = SIGNATURE_INIT;
+	
+	if (!payload || !signature || !sigc)
+		BUG("invalid payload or signature sent !");
+
+	strbuf_addstr(&(sig.sig), signature);
+	sig.result = 'N';
+	sig.st = default_type;
+
+	add_signature(&sigs, &sig);
+
+	status = verify_buffer_signatures(payload, plen, &sigs);
+
+	/*  Some how gpg.format is not sometimes applied, temporary fix to loop and STs */
+	if (status)
+	{
+		for (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++)
+		{
+			sig.st = st;
+			status = verify_buffer_signatures(payload, plen, &sigs);
+			if (!status || sig.result != 'N')
+				break;
+		}
+	}
+	status |= sig.result != 'G' && sig.result != 'U';
+
+	if (sig.signer && !sigc->signer)
+		sigc->signer = xstrdup(sig.signer);
+	if (sig.key && !sigc->key)
+		sigc->key = xstrdup(sig.key);
+	if (sig.fingerprint && !sigc->fingerprint)
+		sigc->fingerprint = xstrdup(sig.fingerprint);
+	if (sig.primary_key_fingerprint && !sigc->primary_key_fingerprint)
+		sigc->primary_key_fingerprint = xstrdup(sig.primary_key_fingerprint);	
+
+	sigc->st = sig.st;
+	sigc->result = sig.result;
+	
+	strbuf_addstr(&(sigc->sig), payload);
+	strbuf_addstr(&(sigc->output), sig.output.buf);
+	strbuf_addstr(&(sigc->status), sig.status.buf);
+
+	return !!status;
+}
+
+size_t strbuf_append_signatures(struct strbuf *buf, const struct signatures *sigs)
+{
+	size_t i;
+	struct signature *psig;
+
+	if (!buf)
+		BUG("invalid buffer passed to signature append function");
+
+	if (!sigs)
+		return 0;
+
+	for (i = 0; i < sigs->nsigs; i++) {
+		psig = sigs->sigs[i];
+		strbuf_addbuf(buf, &(psig->sig));
+	}
+
+	return sigs->nsigs;
+}
+
+void print_signatures(const struct signatures *sigs, unsigned flags)
+{
+	size_t i;
+	const struct signing_tool *tool;
+	const struct signature *psig;
+
+	if (!sigs)
+		error("invalid signatures passed to verify function");
+
+	for (i = 0; i < sigs->nsigs; i++) {
+		psig = sigs->sigs[i];
+		tool = signing_tools[psig->st];
+
+		if (!tool || !tool->print)
+			BUG("signing tool %s undefined", signature_type_name(psig->st));
+
+		tool->print(psig, flags);
+	}
+}
+
+void print_signature_buffer(const struct signature *sigc, unsigned flags)
+{
+	const struct signing_tool *tool;
+
+	if (!sigc)
+		error("invalid signatures passed to verify function");
+
+	tool = signing_tools[default_type];
+
+	if (!tool || !tool->print)
+		BUG("signing tool %s undefined", signature_type_name(sigc->st));
+
+	tool->print(sigc, flags);
+}
+
+enum signature_type signature_type_by_name(const char *name)
+{
+	enum signature_type st;
+
+	if (!name)
+		return default_type;
+
+	for (st = SIGNATURE_TYPE_FIRST; st < SIGNATURE_TYPE_LAST; st++)
+		if (!strcmp(signing_tools[st]->name, name))
+			return st;
+
+	return error("unknown signature type: %s", name);
+}
+
+const char *signature_type_name(enum signature_type st)
+{
+	if (!VALID_SIGNATURE_TYPE(st))
+		return unknown_signature_type;
+
+	return signing_tools[st]->name;
+}
+
+int git_signing_config(const char *var, const char *value, void *cb)
+{
+	int ret = 0;
+	char *t1, *t2, *t3, *buf;
+	enum signature_type st;
+	const struct signing_tool *tool;
+
+	/* user.signingkey is a deprecated alias for signing.<signing.default>.key */
+	if (!strcmp(var, "user.signingkey")) {
+		if (!value)
+			return config_error_nonbool(var);
+		
+		set_signing_key(value, default_type);
+
+		return 0;
+	}
+
+	/* gpg.format is a deprecated alias for signing.default */
+	if (!strcmp(var, "gpg.format") || !strcmp(var, "signing.default")) {
+		if (!value)
+			return config_error_nonbool(var);
+
+		if (!VALID_SIGNATURE_TYPE((st = signature_type_by_name(value))))
+			return config_error_nonbool(var);
+
+		set_signature_type(st);
+
+		return 0;
+	}
+
+	/* gpg.program is a deprecated alias for signing.openpgp.program */
+	if (!strcmp(var, "gpg.program") || !strcmp(var, "signing.openpgp.program")) {
+		ret = signing_tools[OPENPGP_SIGNATURE]->config(
+				"program", value, cb);
+
+		return ret;
+	}
+
+	/* gpg.x509.program is a deprecated alias for signing.x509.program */
+	if (!strcmp(var, "gpg.x509.program") || !strcmp(var, "signing.x509.program")) {
+		ret = signing_tools[X509_SIGNATURE]->config(
+				"program", value, cb);
+
+		return ret;
+	}
+
+	buf = xstrdup(var);
+	t1 = strtok(buf, ".");
+	t2 = strtok(NULL, ".");
+	t3 = strtok(NULL, ".");
+
+	/* gpg.<format>.* is a deprecated alias for signing.<format>.* */
+	if (!strcmp(t1, "gpg") || !strcmp(t1, "signing")) {
+		if (!VALID_SIGNATURE_TYPE((st = signature_type_by_name(t2)))) {
+			free(buf);
+			return error("unsupported variable: %s", var);
+		}
+
+		tool = signing_tools[st];
+		if (!tool || !tool->config) {
+			free(buf);
+			BUG("signing tool %s undefined", signature_type_name(tool->st));
+		}
+
+		ret = tool->config(t3, value, cb);
+	}
+
+	free(buf);
+	return ret;
+}
+
+void set_signing_key(const char *key, enum signature_type st)
+{
+	/*
+	 * Make sure we track the latest default signing key so that if the
+	 * default signing format changes after this, we can make sure the
+	 * default signing tool knows the key to use.
+	 */
+	free(default_signing_key);
+	default_signing_key = xstrdup(key);
+
+	if (!VALID_SIGNATURE_TYPE(st))
+		signing_tools[default_type]->set_key(key);
+	else
+		signing_tools[st]->set_key(key);
+}
+
+const char *get_signing_key(enum signature_type st)
+{
+	if (!VALID_SIGNATURE_TYPE(st))
+		return signing_tools[default_type]->get_key();
+
+	return signing_tools[default_type]->get_key();
+}
+
+void set_signing_program(const char *signing_program, enum signature_type st)
+{
+	/*
+	 * Make sure we track the latest default signing program so that if the
+	 * default signing format changes after this, we can make sure the
+	 * default signing tool knows the program to use.
+	 */
+
+	if (!VALID_SIGNATURE_TYPE(st))
+		signing_tools[default_type]->set_program(signing_program);
+	else
+		signing_tools[st]->set_program(signing_program);
+}
+
+const char *get_signing_program(enum signature_type st)
+{
+	const char *signing_program = NULL;
+
+	if (!VALID_SIGNATURE_TYPE(st)) {
+		signing_program = signing_tools[default_type]->get_program();
+
+		return signing_program;
+	}
+
+	signing_program = signing_tools[st]->get_program();
+
+	return signing_program;
+}
+
+void set_signature_type(enum signature_type st)
+{
+	if (!VALID_SIGNATURE_TYPE(st))
+		return;
+
+	default_type = st;
+
+	/* 
+	 * If the signing key has been set, then make sure the new default
+	 * signing tool knows about it. this fixes the order of operations
+	 * error of parsing the default signing key and default signing
+	 * format in arbitrary order.
+	 */
+	if (default_signing_key) {
+		set_signing_key(default_signing_key, default_type);
+	}
+}
+
+enum signature_type get_signature_type(void)
+{
+	return default_type;
+}
\ No newline at end of file
diff --git a/signing-interface.h b/signing-interface.h
new file mode 100644
index 000000000..b55edbdb8
--- /dev/null
+++ b/signing-interface.h
@@ -0,0 +1,151 @@
+#ifndef SIGNING_INTERFACE_H
+#define SIGNING_INTERFACE_H
+
+struct strbuf;
+
+#define OUTPUT_VERBOSE		1
+#define OUTPUT_RAW			2
+#define OUTPUT_OMIT_STATUS	4
+
+enum signature_type {
+	OPENPGP_SIGNATURE,
+	X509_SIGNATURE,
+
+	SIGNATURE_TYPE_LAST,
+	SIGNATURE_TYPE_FIRST = OPENPGP_SIGNATURE,
+	SIGNATURE_TYPE_COUNT = SIGNATURE_TYPE_LAST - SIGNATURE_TYPE_FIRST,
+	SIGNATURE_TYPE_DEFAULT = OPENPGP_SIGNATURE,
+	SIGNATURE_TYPE_UNKNOWN = -1
+};
+enum signature_type default_type;
+
+#define VALID_SIGNATURE_TYPE(x) \
+	((x >= SIGNATURE_TYPE_FIRST) && (x < SIGNATURE_TYPE_LAST))
+
+struct signature {
+	struct strbuf sig;
+	struct strbuf output;
+	struct strbuf status;
+	enum signature_type st;
+
+	/*
+	 * possible "result":
+	 * 0 (not checked)
+	 * N (checked but no further result)
+	 * U (untrusted good)
+	 * G (good)
+	 * B (bad)
+	 */
+	char result;
+	char *signer;
+	char *key;
+	char *fingerprint;
+	char *primary_key_fingerprint;
+};
+
+struct signatures {
+	size_t nsigs;
+	size_t alloc;
+	struct signature **sigs;
+};
+
+#define SIGNATURES_INIT  { .nsigs = 0, .alloc = 0, .sigs = NULL }
+#define SIGNATURE_INIT  { .sig = STRBUF_INIT, .output = STRBUF_INIT, .status = STRBUF_INIT, .st = OPENPGP_SIGNATURE, .result = '0', .signer = NULL, .key = NULL }
+
+void signatures_clear(struct signatures *sigs);
+void signature_clear(struct signature *sig);
+
+/*
+ * Create a detached signature for the contents of "payload" and append
+ * it to the list of signatures in "sigs". The signature type determines which
+ * type of signature to create and the optional "signing_key" specifies
+ * the key. If no signing key is specified the default key from the
+ * config will be used. If no default is found, then an error is
+ * returned. If the signing operation fails an error is returned.
+ */
+int sign_payload(const char *payload, size_t size, struct signatures *sigs,
+		enum signature_type st, const char *signing_key);
+
+/*
+ * Bridge function to be called by the git code for buffer signature
+ */
+int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
+
+/* 
+ * Look at the signed content (e.g. a signed tag object), whose payload
+ * is followed by one or more detached signatures. Return the offset of
+ * the first signature, or the size of the buf when there are no 
+ * signatures. If a valid signatures struct is passed in, the signatures 
+ * will be parsed and copied into its array of sigs.
+ */
+size_t parse_signatures(const char *payload, size_t size,
+		struct signatures *sigs);
+
+/*
+ * Bridge function to be called by the git code for parsing signatures in a buffer
+ */
+size_t parse_signature(const char *buf, size_t size);
+
+/*
+ * Run the signature verification tools to see if the payload matches
+ * the detached signatures. The output and status of the of the checks
+ * is recorded in the signatures struct. The caller must use
+ * parse_signatures or sign_buffer to initialize the signatures struct
+ * before calling this function.
+ */
+int verify_signed_buffer(const char *payload, size_t payload_size,
+			 const char *signature, size_t signature_size,
+			 struct strbuf *output, struct strbuf *status);
+
+/*
+ * Verify multiple signatures in a single buffer
+ */
+int verify_buffer_signatures(const char *payload, size_t size,
+		struct signatures *sigs);
+
+/*
+ * Bridge function to be called by the git code to verify a signed payload
+ */
+int check_signature(const char *payload, size_t plen, const char *signature,
+	size_t slen, struct signature *sigc);
+
+/*
+ * Prints the results of either signing or verifying the payload in the
+ * signatures struct. If the OUTPUT_VERBOSE flag is specified, then the
+ * payload is printed to stdout. If the OUTPUT_RAW flag is specified, 
+ * the raw status output from the signing tool is printed to stderr, 
+ * otherwise, the nice results from the tool is printed to stderr.
+ */
+void print_signatures(const struct signatures *sigs, unsigned flags);
+
+/*
+ * Bridge function to be called by the git code to print a signature
+ */
+void print_signature_buffer(const struct signature *sigc, unsigned flags);
+
+/*
+ * Appends each of the detached signatures to the end of the strbuf
+ * passed in. Returns the number of signatures appended to the buffer.
+ */
+size_t strbuf_append_signatures(struct strbuf *buf, const struct signatures *sigs);
+
+/*
+ * Translate the name of the signature tool into the enumerated value
+ * for the signature type.
+ */
+enum signature_type signature_type_by_name(const char *name);
+const char *signature_type_name(enum signature_type st);
+
+/*
+ * Config related functions
+ */
+int git_signing_config(const char *var, const char *value, void *cb);
+void set_signing_key(const char *key, enum signature_type st);
+const char *get_signing_key(enum signature_type st);
+void set_signing_program(const char *program, enum signature_type st);
+const char *get_signing_program(enum signature_type st);
+void set_signature_type(enum signature_type st);
+enum signature_type get_signature_type(void);
+
+#endif
+
diff --git a/signing-tool-openpgp.c b/signing-tool-openpgp.c
new file mode 100644
index 000000000..93b63b36d
--- /dev/null
+++ b/signing-tool-openpgp.c
@@ -0,0 +1,409 @@
+#include "cache.h"
+#include "config.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "signing-interface.h"
+#include "signing-tool.h"
+#include "sigchain.h"
+#include "tempfile.h"
+
+static int openpgp_sign(const char *payload, size_t size,
+		struct signature **sig, const char *key);
+static size_t openpgp_parse(const char *payload, size_t size,
+		struct signature **sig);
+static int openpgp_verify(const char *payload, size_t size,
+		struct signature *sig);
+static void openpgp_print(const struct signature *sig, unsigned flags);
+static int openpgp_config(const char *, const char *, void *);
+static void openpgp_set_key(const char *);
+static const char *openpgp_get_key(void);
+static void openpgp_set_program(const char *);
+static const char *openpgp_get_program(void);
+
+const struct signing_tool openpgp_tool = {
+	.st = OPENPGP_SIGNATURE,
+	.name = "openpgp",
+	.sign = &openpgp_sign,
+	.parse = &openpgp_parse,
+	.verify = &openpgp_verify,
+	.print = &openpgp_print,
+	.config = &openpgp_config,
+	.set_key = &openpgp_set_key,
+	.get_key = &openpgp_get_key,
+	.set_program = &openpgp_set_program,
+	.get_program = &openpgp_get_program
+};
+
+static const char *program = "gpg";
+static const char *signing_key = NULL;
+static const char *keyring = NULL;
+static int no_default_keyring = 0;
+struct regex_pattern {
+	const char * begin;
+	const char * end;
+};
+static struct regex_pattern patterns[2] = {
+	{ "^-----BEGIN PGP SIGNATURE-----\n", "-----END PGP SIGNATURE-----\n" },
+	{ "^-----BEGIN PGP MESSAGE-----\n", "-----END PGP MESSAGE-----\n" }
+};
+
+static int openpgp_sign(const char *payload, size_t size,
+		struct signature **sig, const char *key)
+{
+	struct child_process gpg = CHILD_PROCESS_INIT;
+	struct signature *psig;
+	struct strbuf *psignature, *pstatus;
+	int ret;
+	size_t i, j;
+	const char *skey = (!key || !*key) ? signing_key : key;
+
+	/*
+	 * Create the signature.
+	 */
+	if (sig) {
+		psig = *sig;
+		strbuf_init(&(psig->sig), 0);
+		strbuf_init(&(psig->output), 0);
+		strbuf_init(&(psig->status), 0);
+		psig->st = OPENPGP_SIGNATURE;
+		psig->result = 0;
+		psig->signer = NULL;
+		psig->key = NULL;
+		psignature = &(psig->sig);
+		pstatus = &(psig->status);
+	} else {
+		psignature = NULL;
+		pstatus = NULL;
+	}
+
+	argv_array_pushl(&gpg.args,
+			program,
+			"--status-fd=2",
+			"-bsau", skey,
+			NULL);
+
+	/*
+	 * When the username signingkey is bad, program could be terminated
+	 * because gpg exits without reading and then write gets SIGPIPE.
+	 */
+	sigchain_push(SIGPIPE, SIG_IGN);
+	ret = pipe_command(&gpg, payload, size,
+			psignature, 1024, pstatus, 0);
+	sigchain_pop(SIGPIPE);
+
+	if (!sig)
+		return !!ret;
+
+	/* Check for success status from gpg */
+	ret |= !strstr(pstatus->buf, "\n[GNUPG:] SIG_CREATED ");
+
+	if (ret)
+		return error(_("gpg failed to sign the data"));
+
+	/* Mark the signature as good */
+	psig->result = 'G';
+
+	/* Strip CR from the line endings, in case we are on Windows. */
+	for (i = j = 0; i < psig->sig.len; i++)
+		if (psig->sig.buf[i] != '\r') {
+			if (i != j)
+				psig->sig.buf[j] = psig->sig.buf[i];
+			j++;
+		}
+	strbuf_setlen(&(psig->sig), j);
+
+	/* Store the key we used */
+	psig->key = xstrdup(skey);
+
+	return 0;
+}
+
+/*
+ * To get all OpenPGP signatures in a payload, repeatedly call this function
+ * giving it the remainder of the payload as the payload pointer. The return
+ * value is the index of the first char of the signature in the payload. If
+ * no signature is found, size is returned.
+ */
+static size_t openpgp_parse(const char *payload, size_t size,
+		struct signature **sig)
+{
+	int i, ret;
+	regex_t rbegin;
+	regex_t rend;
+	regmatch_t bmatch;
+	regmatch_t ematch;
+	size_t begin, end;
+	struct signature *psig;
+	static char errbuf[1024];
+
+	if (size == 0)
+		return size;
+
+	/*
+	 * Figure out if any OpenPGP signatures are in the payload and which
+	 * begin pattern matches the first signature in the payload.
+	 */
+	for (i = 0; i < ARRAY_SIZE(patterns); i++) {
+		if ((ret = regcomp(&rbegin, patterns[i].begin, REG_EXTENDED|REG_NEWLINE))) {
+			regerror(ret, &rbegin, errbuf, 1024);
+			BUG("Failed to compile regex: %s\n", errbuf);
+
+			return size;
+		}
+		if ((ret = regcomp(&rend, patterns[i].end, REG_EXTENDED|REG_NEWLINE))) {
+			regerror(ret, &rend, errbuf, 1024);
+			BUG("Failed to compile regex: %s\n", errbuf);
+
+			return size;
+		}
+
+		begin = end = 0;
+		if (regexec(&rbegin, payload, 1, &bmatch, 0) ||
+			regexec(&rend, payload, 1, &ematch, 0)) {
+			begin = size;
+			continue;
+		}
+		begin = bmatch.rm_so;
+		end = ematch.rm_eo;
+
+		break;
+	}
+	if (begin == size)
+		goto next;
+
+	/*
+	 * Create the signature.
+	 */
+	if (sig) {
+		psig = *sig;
+		psig = xmalloc(sizeof(struct signature));
+		strbuf_init(&(psig->sig), end - begin);
+		strbuf_add(&(psig->sig), payload + begin, end - begin);
+		strbuf_init(&(psig->output), 0);
+		strbuf_init(&(psig->status), 0);
+		psig->st = OPENPGP_SIGNATURE;
+		psig->result = 0;
+		psig->signer = NULL;
+		psig->key = NULL;
+	}
+	next:
+		regfree(&rbegin);
+		regfree(&rend);
+
+	return begin;
+}
+
+/* An exclusive status -- only one of them can appear in output */
+#define GPG_STATUS_EXCLUSIVE	(1<<0)
+/* The status includes key identifier */
+#define GPG_STATUS_KEYID	(1<<1)
+/* The status includes user identifier */
+#define GPG_STATUS_UID		(1<<2)
+/* The status includes key fingerprints */
+#define GPG_STATUS_FINGERPRINT	(1<<3)
+
+/* Short-hand for standard exclusive *SIG status with keyid & UID */
+#define GPG_STATUS_STDSIG	(GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)
+
+static struct {
+	char result;
+	const char *check;
+	unsigned int flags;
+} sigcheck_gpg_status[] = {
+	{ 'G', "GOODSIG ", GPG_STATUS_STDSIG },
+	{ 'B', "BADSIG ", GPG_STATUS_STDSIG },
+	{ 'U', "TRUST_NEVER", 0 },
+	{ 'U', "TRUST_UNDEFINED", 0 },
+	{ 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },
+	{ 'X', "EXPSIG ", GPG_STATUS_STDSIG },
+	{ 'Y', "EXPKEYSIG ", GPG_STATUS_STDSIG },
+	{ 'R', "REVKEYSIG ", GPG_STATUS_STDSIG },
+	{ 0, "VALIDSIG ", GPG_STATUS_FINGERPRINT },
+};
+
+static void parse_output(struct signature *sigc)
+{
+	const char *buf = sigc->status.buf;
+	const char *line, *next;
+	int i, j;
+	int seen_exclusive_status = 0;
+
+	/* Iterate over all lines */
+	for (line = buf; *line; line = strchrnul(line+1, '\n')) {
+		while (*line == '\n')
+			line++;
+		/* Skip lines that don't start with GNUPG status */
+		if (!skip_prefix(line, "[GNUPG:] ", &line))
+			continue;
+
+		/* Iterate over all search strings */
+		for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
+			if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {
+				if (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {
+					if (seen_exclusive_status++)
+						goto found_duplicate_status;
+				}
+
+				if (sigcheck_gpg_status[i].result)
+					sigc->result = sigcheck_gpg_status[i].result;
+				/* Do we have key information? */
+				if (sigcheck_gpg_status[i].flags & GPG_STATUS_KEYID) {
+					next = strchrnul(line, ' ');
+					free(sigc->key);
+					sigc->key = xmemdupz(line, next - line);
+					/* Do we have signer information? */
+					if (*next && (sigcheck_gpg_status[i].flags & GPG_STATUS_UID)) {
+						line = next + 1;
+						next = strchrnul(line, '\n');
+						free(sigc->signer);
+						sigc->signer = xmemdupz(line, next - line);
+					}
+				}
+				/* Do we have fingerprint? */
+				if (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {
+					next = strchrnul(line, ' ');
+					free(sigc->fingerprint);
+					sigc->fingerprint = xmemdupz(line, next - line);
+
+					/* Skip interim fields */
+					for (j = 9; j > 0; j--) {
+						if (!*next)
+							break;
+						line = next + 1;
+						next = strchrnul(line, ' ');
+					}
+
+					next = strchrnul(line, '\n');
+					free(sigc->primary_key_fingerprint);
+					sigc->primary_key_fingerprint = xmemdupz(line, next - line);
+				}
+
+				break;
+			}
+		}
+	}
+	return;
+
+found_duplicate_status:
+	/*
+	 * GOODSIG, BADSIG etc. can occur only once for each signature.
+	 * Therefore, if we had more than one then we're dealing with multiple
+	 * signatures.  We don't support them currently, and they're rather
+	 * hard to create, so something is likely fishy and we should reject
+	 * them altogether.
+	 */
+	sigc->result = 'E';
+	/* Clear partial data to avoid confusion */
+	FREE_AND_NULL(sigc->primary_key_fingerprint);
+	FREE_AND_NULL(sigc->fingerprint);
+	FREE_AND_NULL(sigc->signer);
+	FREE_AND_NULL(sigc->key);
+}
+
+static int openpgp_verify(const char *payload, size_t size,
+		struct signature *sig)
+{
+	struct child_process gpg = CHILD_PROCESS_INIT;
+	struct tempfile *temp;
+	int ret;
+
+	temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
+	if (!temp)
+		return error_errno(_("could not create temporary file"));
+	if (write_in_full(temp->fd, sig->sig.buf, sig->sig.len) < 0 ||
+	    close_tempfile_gently(temp) < 0) {
+		error_errno(_("failed writing detached signature to '%s'"),
+				temp->filename.buf);
+		delete_tempfile(&temp);
+		return -1;
+	}
+
+	argv_array_push(&gpg.args, program);
+	if (keyring)
+		argv_array_pushl(&gpg.args, "--keyring", keyring, NULL);
+	if (no_default_keyring)
+		argv_array_push(&gpg.args, "--no-default-keyring");
+	argv_array_pushl(&gpg.args,
+			"--keyid-format=long",
+			"--status-fd=1",
+			"--verify", temp->filename.buf, "-",
+			NULL);
+
+	strbuf_reset(&(sig->status));
+	strbuf_reset(&(sig->output));
+
+	sigchain_push(SIGPIPE, SIG_IGN);
+	ret = pipe_command(&gpg, payload, size,
+			&(sig->status), 0, &(sig->output), 0);
+	sigchain_pop(SIGPIPE);
+
+	delete_tempfile(&temp);
+
+	ret |= !strstr(sig->status.buf, "\n[GNUPG:] GOODSIG ");
+
+	if (ret && !sig->output.len)
+		return !!ret;
+
+	parse_output(sig);
+
+	ret |= sig->result != 'G' && sig->result != 'U';
+
+	return !!ret;
+}
+
+static void openpgp_print(const struct signature *sig, unsigned flags)
+{
+	const char *output = flags & OUTPUT_RAW ?
+		sig->status.buf : sig->output.buf;
+
+	if (flags & OUTPUT_VERBOSE && sig->sig.buf)
+		fputs(sig->sig.buf, stdout);
+
+	if (output)
+		fputs(output, stderr);
+}
+
+static int openpgp_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "program"))
+		return git_config_string(&program, var, value);
+
+	if (!strcmp(var, "key"))
+		return git_config_string(&signing_key, var, value);
+
+	if (!strcmp(var, "keyring"))
+		return git_config_string(&keyring, var, value);
+
+	if (!strcmp(var, "nodefaultkeyring")) {
+		no_default_keyring = git_config_bool(var, value);
+		return 0;
+	}
+	return 0;
+}
+
+static void openpgp_set_key(const char *key)
+{
+	free((void*)signing_key);
+	signing_key = xstrdup(key);
+}
+
+static const char *openpgp_get_key(void)
+{
+	if (signing_key)
+		return signing_key;
+	return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
+}
+
+static void openpgp_set_program(const char *signing_program)
+{
+	free((void*)program);
+	program = xstrdup(signing_program);
+}
+
+
+static const char *openpgp_get_program(void)
+{
+	if (program)
+		return program;
+	return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
+}
\ No newline at end of file
diff --git a/signing-tool-x509.c b/signing-tool-x509.c
new file mode 100644
index 000000000..b7de56924
--- /dev/null
+++ b/signing-tool-x509.c
@@ -0,0 +1,383 @@
+#include "cache.h"
+#include "config.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "signing-interface.h"
+#include "signing-tool.h"
+#include "sigchain.h"
+#include "tempfile.h"
+
+static int x509_sign(const char *payload, size_t size,
+		struct signature **sig, const char *key);
+static size_t x509_parse(const char *payload, size_t size,
+		struct signature **sig);
+static int x509_verify(const char *payload, size_t size,
+		struct signature *sig);
+static void x509_print(const struct signature *sig, unsigned flags);
+static int x509_config(const char *, const char *, void *);
+static void x509_set_key(const char *);
+static const char *x509_get_key(void);
+static void x509_set_program(const char *);
+static const char *x509_get_program(void);
+
+const struct signing_tool x509_tool = {
+	.st = X509_SIGNATURE,
+	.name = "x509",
+	.sign = &x509_sign,
+	.parse = &x509_parse,
+	.verify = &x509_verify,
+	.print = &x509_print,
+	.config = &x509_config,
+	.set_key = &x509_set_key,
+	.get_key = &x509_get_key,
+	.set_program = &x509_set_program,
+	.get_program = &x509_get_program
+};
+
+static const char *program = "gpgsm";
+static const char *signing_key = NULL;
+struct regex_pattern {
+	const char * begin;
+	const char * end;
+};
+static struct regex_pattern pattern = {
+	"^-----BEGIN SIGNED MESSAGE-----\n",
+	"^-----END SIGNED MESSAGE-----\n"
+};
+
+static int x509_sign(const char *payload, size_t size,
+		struct signature **sig, const char *key)
+{
+	struct child_process gpgsm = CHILD_PROCESS_INIT;
+	struct signature *psig;
+	struct strbuf *psignature, *pstatus;
+	int ret;
+	size_t i, j;
+	const char *skey = (!key || !*key) ? signing_key : key;
+
+	/*
+	 * Create the signature.
+	 */
+	if (sig) {
+		psig = *sig;
+		strbuf_init(&(psig->sig), 0);
+		strbuf_init(&(psig->output), 0);
+		strbuf_init(&(psig->status), 0);
+		psig->st = X509_SIGNATURE;
+		psig->result = 0;
+		psig->signer = NULL;
+		psig->key = NULL;
+		psignature = &(psig->sig);
+		pstatus = &(psig->status);
+	} else {
+		psignature = NULL;
+		pstatus = NULL;
+	}
+
+	argv_array_pushl(&gpgsm.args,
+			program,
+			"--status-fd=2",
+			"-bsau", skey,
+			NULL);
+
+	/*
+	 * When the username signingkey is bad, program could be terminated
+	 * because gpgsm exits without reading and then write gets SIGPIPE.
+	 */
+	sigchain_push(SIGPIPE, SIG_IGN);
+	ret = pipe_command(&gpgsm, payload, size,
+			psignature, 1024, pstatus, 0);
+	sigchain_pop(SIGPIPE);
+
+	if (!sig)
+		return !!ret;
+
+	ret |= !strstr(pstatus->buf, "\n[GNUPG:] SIG_CREATED ");
+	if (ret)
+		return error(_("gpgsm failed to sign the data"));
+
+	/* Mark the signature as good. */
+	psig->result = 'G';
+
+	/* Strip CR from the line endings, in case we are on Windows. */
+	for (i = j = 0; i < psig->sig.len; i++)
+		if (psig->sig.buf[i] != '\r') {
+			if (i != j)
+				psig->sig.buf[j] = psig->sig.buf[i];
+			j++;
+		}
+	strbuf_setlen(&(psig->sig), j);
+
+	/* Store the key we used */
+	psig->key = xstrdup(skey);
+
+	return 0;
+}
+
+static size_t x509_parse(const char *payload, size_t size,
+		struct signature **sig)
+{
+	int ret;
+	regex_t rbegin;
+	regex_t rend;
+	regmatch_t bmatch;
+	regmatch_t ematch;
+	size_t begin, end;
+	struct signature *psig;
+	static char errbuf[1024];
+
+	if (size == 0)
+		return size;
+
+	/*
+	 * Find the first x509 signature in the payload and copy it into the
+	 * signature struct.
+	 */
+	if ((ret = regcomp(&rbegin, pattern.begin, REG_EXTENDED|REG_NEWLINE))) {
+		regerror(ret, &rbegin, errbuf, 1024);
+		BUG("Failed to compile regex: %s\n", errbuf);
+
+		return size;
+	}
+	if ((ret = regcomp(&rend, pattern.end, REG_EXTENDED|REG_NEWLINE))) {
+		regerror(ret, &rend, errbuf, 1024);
+		BUG("Failed to compile regex: %s\n", errbuf);
+
+		return size;
+	}
+
+	begin = end = 0;
+	if (regexec(&rbegin, payload, 1, &bmatch, 0) ||
+		regexec(&rend, payload, 1, &ematch, 0)) {
+		begin = size;
+	}
+	if (begin == size)
+		goto next;
+
+	begin = bmatch.rm_so;
+	end = ematch.rm_eo;
+
+	/*
+	 * Create the signature.
+	 */
+	if (sig) {
+		psig = *sig;
+		psig = xmalloc(sizeof(struct signature));
+		strbuf_init(&(psig->sig), end - begin);
+		strbuf_add(&(psig->sig), payload + begin, end - begin);
+		strbuf_init(&(psig->output), 0);
+		strbuf_init(&(psig->status), 0);
+		psig->st = X509_SIGNATURE;
+		psig->result = 0;
+		psig->signer = NULL;
+		psig->key = NULL;
+	}
+
+	next:
+		regfree(&rbegin);
+		regfree(&rend);
+
+	return begin;
+}
+
+/* An exclusive status -- only one of them can appear in output */
+#define GPG_STATUS_EXCLUSIVE	(1<<0)
+/* The status includes key identifier */
+#define GPG_STATUS_KEYID	(1<<1)
+/* The status includes user identifier */
+#define GPG_STATUS_UID		(1<<2)
+/* The status includes key fingerprints */
+#define GPG_STATUS_FINGERPRINT	(1<<3)
+
+/* Short-hand for standard exclusive *SIG status with keyid & UID */
+#define GPG_STATUS_STDSIG	(GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)
+
+static struct {
+	char result;
+	const char *check;
+	unsigned int flags;
+} sigcheck_gpg_status[] = {
+	{ 'G', "GOODSIG ", GPG_STATUS_STDSIG },
+	{ 'B', "BADSIG ", GPG_STATUS_STDSIG },
+	{ 'U', "TRUST_NEVER", 0 },
+	{ 'U', "TRUST_UNDEFINED", 0 },
+	{ 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },
+	{ 'X', "EXPSIG ", GPG_STATUS_STDSIG },
+	{ 'Y', "EXPKEYSIG ", GPG_STATUS_STDSIG },
+	{ 'R', "REVKEYSIG ", GPG_STATUS_STDSIG },
+	{ 0, "VALIDSIG ", GPG_STATUS_FINGERPRINT },
+};
+
+static void parse_output(struct signature *sigc)
+{
+	const char *buf = sigc->status.buf;
+	const char *line, *next;
+	int i, j;
+	int seen_exclusive_status = 0;
+
+	/* Iterate over all lines */
+	for (line = buf; *line; line = strchrnul(line+1, '\n')) {
+		while (*line == '\n')
+			line++;
+		/* Skip lines that don't start with GNUPG status */
+		if (!skip_prefix(line, "[GNUPG:] ", &line))
+			continue;
+
+		/* Iterate over all search strings */
+		for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
+			if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {
+				if (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {
+					if (seen_exclusive_status++)
+						goto found_duplicate_status;
+				}
+
+				if (sigcheck_gpg_status[i].result)
+					sigc->result = sigcheck_gpg_status[i].result;
+				/* Do we have key information? */
+				if (sigcheck_gpg_status[i].flags & GPG_STATUS_KEYID) {
+					next = strchrnul(line, ' ');
+					free(sigc->key);
+					sigc->key = xmemdupz(line, next - line);
+					/* Do we have signer information? */
+					if (*next && (sigcheck_gpg_status[i].flags & GPG_STATUS_UID)) {
+						line = next + 1;
+						next = strchrnul(line, '\n');
+						free(sigc->signer);
+						sigc->signer = xmemdupz(line, next - line);
+					}
+				}
+				/* Do we have fingerprint? */
+				if (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {
+					next = strchrnul(line, ' ');
+					free(sigc->fingerprint);
+					sigc->fingerprint = xmemdupz(line, next - line);
+
+					/* Skip interim fields */
+					for (j = 9; j > 0; j--) {
+						if (!*next)
+							break;
+						line = next + 1;
+						next = strchrnul(line, ' ');
+					}
+
+					next = strchrnul(line, '\n');
+					free(sigc->primary_key_fingerprint);
+					sigc->primary_key_fingerprint = xmemdupz(line, next - line);
+				}
+
+				break;
+			}
+		}
+	}
+	return;
+
+found_duplicate_status:
+	/*
+	 * GOODSIG, BADSIG etc. can occur only once for each signature.
+	 * Therefore, if we had more than one then we're dealing with multiple
+	 * signatures.  We don't support them currently, and they're rather
+	 * hard to create, so something is likely fishy and we should reject
+	 * them altogether.
+	 */
+	sigc->result = 'E';
+	/* Clear partial data to avoid confusion */
+	FREE_AND_NULL(sigc->primary_key_fingerprint);
+	FREE_AND_NULL(sigc->fingerprint);
+	FREE_AND_NULL(sigc->signer);
+	FREE_AND_NULL(sigc->key);
+}
+
+static int x509_verify(const char *payload, size_t size,
+		struct signature *sig)
+{
+	struct child_process gpgsm = CHILD_PROCESS_INIT;
+	struct tempfile *temp;
+	int ret;
+
+	temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
+	if (!temp)
+		return error_errno(_("could not create temporary file"));
+	if (write_in_full(temp->fd, sig->sig.buf, sig->sig.len) < 0 ||
+	    close_tempfile_gently(temp) < 0) {
+		error_errno(_("failed writing detached signature to '%s'"),
+				temp->filename.buf);
+		delete_tempfile(&temp);
+		return -1;
+	}
+
+	argv_array_push(&gpgsm.args, program);
+	argv_array_pushl(&gpgsm.args,
+			"--status-fd=1",
+			"--verify", temp->filename.buf, "-",
+			NULL);
+
+	strbuf_reset(&(sig->status));
+	strbuf_reset(&(sig->output));
+
+	sigchain_push(SIGPIPE, SIG_IGN);
+	ret = pipe_command(&gpgsm, payload, size,
+			&(sig->status), 0, &(sig->output), 0);
+	sigchain_pop(SIGPIPE);
+
+	delete_tempfile(&temp);
+
+	ret |= !strstr(sig->status.buf, "\n[GNUPG:] GOODSIG ");
+
+	if (ret && !sig->output.len)
+		return !!ret;
+
+	parse_output(sig);
+
+	ret |= sig->result != 'G' && sig->result != 'U';
+
+	return !!ret;
+}
+
+static void x509_print(const struct signature *sig, unsigned flags)
+{
+	const char *output = flags & OUTPUT_RAW ?
+		sig->status.buf : sig->output.buf;
+
+	if (flags & OUTPUT_VERBOSE && sig->sig.buf)
+		fputs(sig->sig.buf, stdout);
+
+	if (output)
+		fputs(output, stderr);
+}
+
+static int x509_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "program"))
+		return git_config_string(&program, var, value);
+
+	if (!strcmp(var, "key"))
+		return git_config_string(&signing_key, var, value);
+
+	return 0;
+}
+
+static void x509_set_key(const char *key)
+{
+	free((void*)signing_key);
+	signing_key = xstrdup(key);
+}
+
+static const char *x509_get_key(void)
+{
+	if (signing_key)
+		return signing_key;
+	return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
+}
+
+static void x509_set_program(const char *signing_program)
+{
+	free((void*)program);
+	program = xstrdup(signing_program);
+}
+
+static const char *x509_get_program(void)
+{
+	if (program)
+		return program;
+	return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
+}
\ No newline at end of file
diff --git a/signing-tool.h b/signing-tool.h
new file mode 100644
index 000000000..ee7ccc7a5
--- /dev/null
+++ b/signing-tool.h
@@ -0,0 +1,35 @@
+#ifndef SIGNING_TOOL_H
+#define SIGNING_TOOL_H
+
+struct strbuf;
+struct signature;
+
+typedef int (*sign_fn)(const char *payload, size_t size,
+	struct signature **sig, const char *key);
+typedef size_t (*parse_fn)(const char *payload, size_t size,
+	struct signature **sig);
+typedef int (*verify_fn)(const char *payload, size_t size,
+	struct signature *sig);
+typedef void (*print_fn)(const struct signature *sig, unsigned flags);
+typedef int (*config_fn)(const char *var, const char *value, void *cb);
+typedef void (*set_key_fn)(const char *key);
+typedef const char *(*get_key_fn)(void);
+typedef void (*set_program_fn)(const char *signing_program);
+typedef const char *(*get_program_fn)(void);
+
+struct signing_tool {
+	const enum signature_type st;
+	const char* name;
+	sign_fn sign;
+	parse_fn parse;
+	verify_fn verify;
+	print_fn print;
+	config_fn config;
+	set_key_fn set_key;
+	get_key_fn get_key;
+	set_program_fn set_program;
+	get_program_fn get_program;
+};
+
+#endif
+
-- 
2.11.0
Next: brian m. carlson
Message 1 of 3 in “Signing API: Added new signing interface API”
  1. 2/5 Signing API: Added new signing interface APIIbrahim El, Aug 26, 2019
  2. brian m. carlsonAug 26, 2019
  3. Ibrahim ElAug 27, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.