diff --git a/Makefile b/Makefile index 24bef8d4282baa7702d4386193c4291275c708c7..432c3dedf2d6f05e3a46c336c0486a95be63d021 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 0000000000000000000000000000000000000000..21a386fe8bd49b164538cdd845238b1c70e4558b --- /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 0000000000000000000000000000000000000000..eac91ae1036f264d2edfe25c98bf38d587ac208a --- /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