This commit adds basic RFC3161 functionality, which is linked to the main git binary. It is used for interaction with git objects by parsing data from and writing data to it. Moreover, it is responsible for passing data to the external helper tool `git-timestamp-util`, which does the actual work of creating and verifying trusted time-stamps. Signed-off-by: Anton Würfel <anton.wuerfel@xxxxxx> Signed-off-by: Phillip Raffeck <phillip.raffeck@xxxxxx> --- Makefile | 1 + rfc3161.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rfc3161.h | 12 ++++ 3 files changed, 232 insertions(+) create mode 100644 rfc3161.c create mode 100644 rfc3161.h diff --git a/Makefile b/Makefile index 24bef8d..432c3de 100644 --- a/Makefile +++ b/Makefile @@ -792,6 +792,7 @@ LIB_OBJS += replace_object.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o LIB_OBJS += revision.o +LIB_OBJS += rfc3161.o LIB_OBJS += run-command.o LIB_OBJS += send-pack.o LIB_OBJS += sequencer.o diff --git a/rfc3161.c b/rfc3161.c new file mode 100644 index 0000000..21a386f --- /dev/null +++ b/rfc3161.c @@ -0,0 +1,219 @@ +#include "cache.h" +#include "commit.h" +#include "run-command.h" +#include "strbuf.h" +#include "gpg-interface.h" +#include "rfc3161.h" + +static const char *timeutil_cmd = "timestamp-util"; +static const char *ts_signature_begin = "-----BEGIN RFC3161-----"; +static const char *ts_signature_end = "-----END RFC3161-----"; + +static void sha1_from_strbuf(struct strbuf *buf, unsigned char sha1[20]); +static void sha1_in_hex(struct strbuf *buf, char sha1_hex[40]); + +static int verify_tsr(char *sha1, struct strbuf *base64); + +/* + * To create a time-stamp signature, get the SHA1 hash of buffer and pass it to + * git-timestamp-util. This helper program returns a minimalized TSR which is + * appended with some metadata and stored to the git object. + */ +int create_time_signature(struct strbuf *buffer, struct strbuf *sig) +{ + ssize_t len; + char sha1_hex[40]; + struct strbuf tsr = STRBUF_INIT; + struct child_process timeutil = CHILD_PROCESS_INIT; + const char *args[] = { + timeutil_cmd, + "-c", + sha1_hex, + NULL + }; + + timeutil.argv = args; + timeutil.in = 0; + timeutil.out = -1; + timeutil.git_cmd = 1; + + sha1_in_hex(buffer, sha1_hex); + + if (start_command(&timeutil)) + return error(_("could not run git-%s"), timeutil_cmd); + + len = strbuf_read(&tsr, timeutil.out, 1024); + close(timeutil.out); + + if (finish_command(&timeutil) || len <= 0) + return 1; + + strbuf_addf(sig, "%s\n", ts_signature_begin); + strbuf_addf(sig, "Version: 1\n\n"); + strbuf_addbuf(sig, &tsr); + strbuf_addf(sig, "%s\n", ts_signature_end); + + strbuf_release(&tsr); + return 0; +} + +/* + * To verify a time-stamp signature, extract the time-stamp from a git object, + * extract the object id and pass the prepared data to git-timestamp-util, which + * will do the verification for us. + */ +int verify_time_signature(const char *buf, unsigned long size) +{ + char sha1_hex[40]; + struct strbuf timesig_base64 = STRBUF_INIT; + struct strbuf payload = STRBUF_INIT; + int ret = 1; + + printf("\n"); + + if (!parse_timestamp(buf, size, ×ig_base64, &payload)) { + printf("time-stamp: no signature found\n"); + return 1; + } + + /* + * Remove possible inline GPG signature from git object. Note that git + * tags do not use inline signatures for historical reasons, so checking + * the return value of remove_signature would be useless. + * Any tag-like signatures outside of the object header are already + * removed by parse_timestamp. + */ + remove_signature(&payload); + sha1_in_hex(&payload, sha1_hex); + + if (verify_tsr(sha1_hex, ×ig_base64)) + goto err; + + ret = 0; +err: + strbuf_release(×ig_base64); + strbuf_release(&payload); + return ret; +} + +int parse_timestamp(const char *buffer, size_t size, struct strbuf *timestamp, + struct strbuf *payload) +{ + int saw_timestamp = -1; + const char *line, *tail, *next; + + line = buffer; + tail = buffer + size; + saw_timestamp = 0; + + /* Search for beginning of time-stamp. */ + while (line < tail) { + next = memchr(line, '\n', tail - line); + + next = next ? next + 1 : tail; + if (starts_with(line, time_sig_header) && + line[time_sig_header_len] == ' ') + break; + + strbuf_add(payload, line, next - line); + line = next; + } + + if (line >= tail) + return saw_timestamp; + + /* + * Found beginning of time-stamp. Do not add -----BEGIN RFC3161-----, + * version information and the separating newline to the buffer. + */ + line = memchr(line, '\n', tail - line) + 1; + line = memchr(line, '\n', tail - line) + 1; + line = memchr(line, '\n', tail - line) + 1; + saw_timestamp = 1; + + /* Read in time-stamp data. */ + while (line < tail) { + next = memchr(line, '\n', tail - line); + + next = next ? next + 1 : tail; + if (line[0] == ' ') + line = line + 1; + else + break; + + /* do not add -----END RFC3161----- to buffer */ + if (starts_with(line, ts_signature_end)) { + line = next; + break; + } + + strbuf_add(timestamp, line, next - line); + line = next; + } + + while (line < tail) { + next = memchr(line, '\n', tail - line); + next = next ? next + 1 : tail; + + if (starts_with(line, PGP_SIGNATURE)) + break; + + strbuf_add(payload, line, next - line); + line = next; + } + + return saw_timestamp; +} + +/* Pass the prepared data to git-timestamp-util */ +static int verify_tsr(char *sha1, struct strbuf *base64) +{ + struct child_process timeutil = CHILD_PROCESS_INIT; + const char *args[] = { + timeutil_cmd, + "-v", + sha1, + NULL + }; + + timeutil.argv = args; + timeutil.in = -1; + timeutil.out = 0; + timeutil.git_cmd = 1; + + if (start_command(&timeutil)) + return error(_("could not run git-%s"), timeutil_cmd); + + if (write_in_full(timeutil.in, base64->buf, base64->len) + != base64->len) { + close(timeutil.in); + finish_command(&timeutil); + return 1; + } + close(timeutil.in); + + if (finish_command(&timeutil)) + return 1; + + return 0; +} + +static void sha1_from_strbuf(struct strbuf *buf, unsigned char sha1[20]) +{ + git_SHA_CTX c; + + git_SHA1_Init(&c); + git_SHA1_Update(&c, buf->buf, buf->len); + git_SHA1_Final(sha1, &c); +} + +static void sha1_in_hex(struct strbuf *buf, char sha1_hex[40]) +{ + char *tmp; + unsigned char sha1[20]; + + sha1_from_strbuf(buf, sha1); + tmp = sha1_to_hex(sha1); + strcpy(sha1_hex, tmp); +} + diff --git a/rfc3161.h b/rfc3161.h new file mode 100644 index 0000000..eac91ae --- /dev/null +++ b/rfc3161.h @@ -0,0 +1,12 @@ +#ifndef RFC3161_H +#define RFC3161_H + +#define time_sig_header "timesig" +#define time_sig_header_len (sizeof(time_sig_header) - 1) + +int create_time_signature(struct strbuf *buffer, struct strbuf *sig); +int verify_time_signature(const char *buf, unsigned long size); +int parse_timestamp(const char *buffer, size_t size, struct strbuf *timestamp, + struct strbuf *payload); + +#endif -- 2.8.0.rc0.62.gfc8aefa.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html