"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Omar Polo <op@omarpolo.com>
Subject:
add some helper functions to compute hashes
To:
gameoftrees@openbsd.org
Date:
Thu, 23 Feb 2023 16:45:40 +0100

Download raw body.

Thread
I'm proposing to a set of functions to abstract over SHA1Init,
SHA1Update, SHA1Final and their SHA256 corrispondent.  They follow
closely the SHA1{Init,Update,Final} interface, to make the switch
easier.  Since most of the times we're interested to build an object
id with the resulting digest, got_hash_final_object_id() is provided
to aid with that.  (It's another bit that will help when the struct
got_object_id will grow.)

Diff below expands inflate.c/deflate.c to optionally use this "hash
context" too, without dropping the SHA1 stuff, but replace all the
other SHA1*() usage.  Dropping the sha1 bits from inflate/deflate will
be done as a separate follow-up diff.

At the moment this should be a no-op in practice, since GOT_HASH_SHA1
is hardcoded everywhere, but soon we'll start to 'bubble up' the
digest type, making most of this working transparently on both sha1
and sha256 repos.

Note that while my immediate interest is to getting object/packfiles
parsing working (i.e. only handling bare repos for reading, not
writing) this should also help for future work on sha256 object
creation, pack generation, and maybe even for gotd :)

In the future we might want to expand these API to compute multiple
digests at the same time (could be required to fetch/send from sha1 to
sha256 or vice-versa), but it's something so down in the list that is
not yet implemented to keep these functions small and easy.

Tests are still fully parsing (without packing, with GOT_TEST_PACK=1,
and GOT_TEST_PACK=ref-delta.  gotd tests are also fine.)

comments/ok?  suggestion for alternate naming?

diffstat /home/op/w/got
 M  gotd/repo_write.c                        |   8+   9-
 M  lib/deflate.c                            |   4+   0-
 M  lib/fileindex.c                          |  42+  40-
 M  lib/got_lib_deflate.h                    |   3+   0-
 M  lib/got_lib_hash.h                       |  25+   0-
 M  lib/got_lib_inflate.h                    |   6+   0-
 M  lib/got_lib_pack_index.h                 |   1+   1-
 M  lib/hash.c                               |  47+   0-
 M  lib/inflate.c                            |   7+   0-
 M  lib/object_create.c                      |  35+  35-
 M  lib/object_parse.c                       |  18+  18-
 M  lib/pack.c                               |  16+  15-
 M  lib/pack_create.c                        |  14+  13-
 M  lib/pack_index.c                         |  32+  32-
 M  libexec/got-fetch-pack/got-fetch-pack.c  |   8+   7-
 M  libexec/got-index-pack/got-index-pack.c  |   1+   0-
 M  libexec/got-read-blob/got-read-blob.c    |   5+   4-

17 files changed, 272 insertions(+), 174 deletions(-)

diff /home/op/w/got
commit - 87a3ab84d3eb87b790e3d34aeec2c344a8d7375b
path + /home/op/w/got
blob - 9534a8dc0cbb055298ed2df9da2f4cc08ae9ad9f
file + gotd/repo_write.c
--- gotd/repo_write.c
+++ gotd/repo_write.c
@@ -44,6 +44,7 @@
 
 #include "got_lib_delta.h"
 #include "got_lib_delta_cache.h"
+#include "got_lib_hash.h"
 #include "got_lib_object.h"
 #include "got_lib_object_cache.h"
 #include "got_lib_ratelimit.h"
@@ -52,8 +53,6 @@
 #include "got_lib_repository.h"
 #include "got_lib_poll.h"
 
-#include "got_lib_hash.h" /* XXX temp include for debugging */
-
 #include "log.h"
 #include "gotd.h"
 #include "repo_write.h"
@@ -480,7 +479,7 @@ copy_object_type_and_size(uint8_t *type, uint64_t *siz
 
 static const struct got_error *
 copy_object_type_and_size(uint8_t *type, uint64_t *size, int infd, int outfd,
-    off_t *outsize, BUF *buf, size_t *buf_pos, SHA1_CTX *ctx)
+    off_t *outsize, BUF *buf, size_t *buf_pos, struct got_hash *ctx)
 {
 	const struct got_error *err = NULL;
 	uint8_t t = 0;
@@ -529,7 +528,7 @@ copy_ref_delta(int infd, int outfd, off_t *outsize, BU
 
 static const struct got_error *
 copy_ref_delta(int infd, int outfd, off_t *outsize, BUF *buf, size_t *buf_pos,
-    SHA1_CTX *ctx)
+    struct got_hash *ctx)
 {
 	const struct got_error *err = NULL;
 	size_t remain = buf_len(buf) - *buf_pos;
@@ -552,7 +551,7 @@ copy_offset_delta(int infd, int outfd, off_t *outsize,
 
 static const struct got_error *
 copy_offset_delta(int infd, int outfd, off_t *outsize, BUF *buf, size_t *buf_pos,
-    SHA1_CTX *ctx)
+    struct got_hash *ctx)
 {
 	const struct got_error *err = NULL;
 	uint64_t o = 0;
@@ -599,7 +598,7 @@ copy_zstream(int infd, int outfd, off_t *outsize, BUF 
 
 static const struct got_error *
 copy_zstream(int infd, int outfd, off_t *outsize, BUF *buf, size_t *buf_pos,
-    SHA1_CTX *ctx)
+    struct got_hash *ctx)
 {
 	const struct got_error *err = NULL;
 	z_stream z;
@@ -703,7 +702,7 @@ recv_packdata(off_t *outsize, uint32_t *nobj, uint8_t 
 	struct got_packfile_hdr hdr;
 	size_t have;
 	uint32_t nhave = 0;
-	SHA1_CTX ctx;
+	struct got_hash ctx;
 	uint8_t expected_sha1[SHA1_DIGEST_LENGTH];
 	char hex[SHA1_DIGEST_STRING_LENGTH];
 	BUF *buf = NULL;
@@ -717,7 +716,7 @@ recv_packdata(off_t *outsize, uint32_t *nobj, uint8_t 
 	if (client->nref_updates == client->nref_del)
 		return NULL;
 
-	SHA1Init(&ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 	err = got_poll_read_full(infd, &have, &hdr, sizeof(hdr), sizeof(hdr));
 	if (err)
@@ -791,7 +790,7 @@ recv_packdata(off_t *outsize, uint32_t *nobj, uint8_t 
 
 	log_debug("received %u objects", *nobj);
 
-	SHA1Final(expected_sha1, &ctx);
+	got_hash_final(&ctx, expected_sha1);
 
 	remain = buf_len(buf) - buf_pos;
 	if (remain < SHA1_DIGEST_LENGTH) {
blob - 2e454602c2119dabd994881cce3b363f4ac0b9ac
file + lib/deflate.c
--- lib/deflate.c
+++ lib/deflate.c
@@ -30,6 +30,7 @@
 #include "got_path.h"
 
 #include "got_lib_deflate.h"
+#include "got_lib_hash.h"
 #include "got_lib_poll.h"
 
 #ifndef MIN
@@ -89,6 +90,9 @@ csum_output(struct got_deflate_checksum *csum, const u
 
 	if (csum->output_sha1)
 		SHA1Update(csum->output_sha1, buf, len);
+
+	if (csum->output_ctx)
+		got_hash_update(csum->output_ctx, buf, len);
 }
 
 const struct got_error *
blob - 8d63257a31ec340aa4c23346b7148d5331d4c107
file + lib/fileindex.c
--- lib/fileindex.c
+++ lib/fileindex.c
@@ -35,6 +35,7 @@
 #include "got_object.h"
 #include "got_path.h"
 
+#include "got_lib_hash.h"
 #include "got_lib_fileindex.h"
 #include "got_lib_worktree.h"
 
@@ -355,12 +356,12 @@ write_fileindex_val64(SHA1_CTX *ctx, uint64_t val, FIL
 }
 
 static const struct got_error *
-write_fileindex_val64(SHA1_CTX *ctx, uint64_t val, FILE *outfile)
+write_fileindex_val64(struct got_hash *ctx, uint64_t val, FILE *outfile)
 {
 	size_t n;
 
 	val = htobe64(val);
-	SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
+	got_hash_update(ctx, &val, sizeof(val));
 	n = fwrite(&val, 1, sizeof(val), outfile);
 	if (n != sizeof(val))
 		return got_ferror(outfile, GOT_ERR_IO);
@@ -368,12 +369,12 @@ write_fileindex_val32(SHA1_CTX *ctx, uint32_t val, FIL
 }
 
 static const struct got_error *
-write_fileindex_val32(SHA1_CTX *ctx, uint32_t val, FILE *outfile)
+write_fileindex_val32(struct got_hash *ctx, uint32_t val, FILE *outfile)
 {
 	size_t n;
 
 	val = htobe32(val);
-	SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
+	got_hash_update(ctx, &val, sizeof(val));
 	n = fwrite(&val, 1, sizeof(val), outfile);
 	if (n != sizeof(val))
 		return got_ferror(outfile, GOT_ERR_IO);
@@ -381,12 +382,12 @@ write_fileindex_val16(SHA1_CTX *ctx, uint16_t val, FIL
 }
 
 static const struct got_error *
-write_fileindex_val16(SHA1_CTX *ctx, uint16_t val, FILE *outfile)
+write_fileindex_val16(struct got_hash *ctx, uint16_t val, FILE *outfile)
 {
 	size_t n;
 
 	val = htobe16(val);
-	SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
+	got_hash_update(ctx, &val, sizeof(val));
 	n = fwrite(&val, 1, sizeof(val), outfile);
 	if (n != sizeof(val))
 		return got_ferror(outfile, GOT_ERR_IO);
@@ -394,7 +395,7 @@ write_fileindex_path(SHA1_CTX *ctx, const char *path, 
 }
 
 static const struct got_error *
-write_fileindex_path(SHA1_CTX *ctx, const char *path, FILE *outfile)
+write_fileindex_path(struct got_hash *ctx, const char *path, FILE *outfile)
 {
 	size_t n, len, pad = 0;
 	static const uint8_t zero[8] = { 0 };
@@ -405,11 +406,11 @@ write_fileindex_path(SHA1_CTX *ctx, const char *path, 
 	if (pad == 0)
 		pad = 8; /* NUL-terminate */
 
-	SHA1Update(ctx, path, len);
+	got_hash_update(ctx, path, len);
 	n = fwrite(path, 1, len, outfile);
 	if (n != len)
 		return got_ferror(outfile, GOT_ERR_IO);
-	SHA1Update(ctx, zero, pad);
+	got_hash_update(ctx, zero, pad);
 	n = fwrite(zero, 1, pad, outfile);
 	if (n != pad)
 		return got_ferror(outfile, GOT_ERR_IO);
@@ -417,7 +418,7 @@ write_fileindex_entry(SHA1_CTX *ctx, struct got_filein
 }
 
 static const struct got_error *
-write_fileindex_entry(SHA1_CTX *ctx, struct got_fileindex_entry *ie,
+write_fileindex_entry(struct got_hash *ctx, struct got_fileindex_entry *ie,
     FILE *outfile)
 {
 	const struct got_error *err;
@@ -451,12 +452,12 @@ write_fileindex_entry(SHA1_CTX *ctx, struct got_filein
 	if (err)
 		return err;
 
-	SHA1Update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
+	got_hash_update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
 	n = fwrite(ie->blob_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
 	if (n != SHA1_DIGEST_LENGTH)
 		return got_ferror(outfile, GOT_ERR_IO);
 
-	SHA1Update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
+	got_hash_update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
 	n = fwrite(ie->commit_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
 	if (n != SHA1_DIGEST_LENGTH)
 		return got_ferror(outfile, GOT_ERR_IO);
@@ -472,7 +473,7 @@ write_fileindex_entry(SHA1_CTX *ctx, struct got_filein
 	stage = got_fileindex_entry_stage_get(ie);
 	if (stage == GOT_FILEIDX_STAGE_MODIFY ||
 	    stage == GOT_FILEIDX_STAGE_ADD) {
-		SHA1Update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
+		got_hash_update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
 		n = fwrite(ie->staged_blob_sha1, 1, SHA1_DIGEST_LENGTH,
 		    outfile);
 		if (n != SHA1_DIGEST_LENGTH)
@@ -487,20 +488,20 @@ got_fileindex_write(struct got_fileindex *fileindex, F
 {
 	const struct got_error *err = NULL;
 	struct got_fileindex_hdr hdr;
-	SHA1_CTX ctx;
-	uint8_t sha1[SHA1_DIGEST_LENGTH];
+	struct got_hash ctx;
+	uint8_t hash[GOT_HASH_DIGEST_MAXLEN];
 	size_t n;
 	struct got_fileindex_entry *ie, *tmp;
 
-	SHA1Init(&ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 	hdr.signature = htobe32(GOT_FILE_INDEX_SIGNATURE);
 	hdr.version = htobe32(GOT_FILE_INDEX_VERSION);
 	hdr.nentries = htobe32(fileindex->nentries);
 
-	SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
-	SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
-	SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
+	got_hash_update(&ctx, &hdr.signature, sizeof(hdr.signature));
+	got_hash_update(&ctx, &hdr.version, sizeof(hdr.version));
+	got_hash_update(&ctx, &hdr.nentries, sizeof(hdr.nentries));
 	n = fwrite(&hdr.signature, 1, sizeof(hdr.signature), outfile);
 	if (n != sizeof(hdr.signature))
 		return got_ferror(outfile, GOT_ERR_IO);
@@ -524,9 +525,9 @@ got_fileindex_write(struct got_fileindex *fileindex, F
 			return err;
 	}
 
-	SHA1Final(sha1, &ctx);
-	n = fwrite(sha1, 1, sizeof(sha1), outfile);
-	if (n != sizeof(sha1))
+	got_hash_final(&ctx, hash);
+	n = fwrite(hash, 1, SHA1_DIGEST_LENGTH, outfile);
+	if (n != SHA1_DIGEST_LENGTH)
 		return got_ferror(outfile, GOT_ERR_IO);
 
 	if (fflush(outfile) != 0)
@@ -536,46 +537,46 @@ read_fileindex_val64(uint64_t *val, SHA1_CTX *ctx, FIL
 }
 
 static const struct got_error *
-read_fileindex_val64(uint64_t *val, SHA1_CTX *ctx, FILE *infile)
+read_fileindex_val64(uint64_t *val, struct got_hash *ctx, FILE *infile)
 {
 	size_t n;
 
 	n = fread(val, 1, sizeof(*val), infile);
 	if (n != sizeof(*val))
 		return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
-	SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
+	got_hash_update(ctx, val, sizeof(*val));
 	*val = be64toh(*val);
 	return NULL;
 }
 
 static const struct got_error *
-read_fileindex_val32(uint32_t *val, SHA1_CTX *ctx, FILE *infile)
+read_fileindex_val32(uint32_t *val, struct got_hash *ctx, FILE *infile)
 {
 	size_t n;
 
 	n = fread(val, 1, sizeof(*val), infile);
 	if (n != sizeof(*val))
 		return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
-	SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
+	got_hash_update(ctx, val, sizeof(*val));
 	*val = be32toh(*val);
 	return NULL;
 }
 
 static const struct got_error *
-read_fileindex_val16(uint16_t *val, SHA1_CTX *ctx, FILE *infile)
+read_fileindex_val16(uint16_t *val, struct got_hash *ctx, FILE *infile)
 {
 	size_t n;
 
 	n = fread(val, 1, sizeof(*val), infile);
 	if (n != sizeof(*val))
 		return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
-	SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
+	got_hash_update(ctx, val, sizeof(*val));
 	*val = be16toh(*val);
 	return NULL;
 }
 
 static const struct got_error *
-read_fileindex_path(char **path, SHA1_CTX *ctx, FILE *infile)
+read_fileindex_path(char **path, struct got_hash *ctx, FILE *infile)
 {
 	const struct got_error *err = NULL;
 	const size_t chunk_size = 8;
@@ -600,7 +601,7 @@ read_fileindex_path(char **path, SHA1_CTX *ctx, FILE *
 			err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
 			break;
 		}
-		SHA1Update(ctx, *path + len, chunk_size);
+		got_hash_update(ctx, *path + len, chunk_size);
 		len += chunk_size;
 	} while (memchr(*path + len - chunk_size, '\0', chunk_size) == NULL);
 
@@ -612,7 +613,7 @@ read_fileindex_entry(struct got_fileindex_entry **iep,
 }
 
 static const struct got_error *
-read_fileindex_entry(struct got_fileindex_entry **iep, SHA1_CTX *ctx,
+read_fileindex_entry(struct got_fileindex_entry **iep, struct got_hash *ctx,
     FILE *infile, uint32_t version)
 {
 	const struct got_error *err;
@@ -657,14 +658,14 @@ read_fileindex_entry(struct got_fileindex_entry **iep,
 		err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
 		goto done;
 	}
-	SHA1Update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
+	got_hash_update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
 
 	n = fread(ie->commit_sha1, 1, SHA1_DIGEST_LENGTH, infile);
 	if (n != SHA1_DIGEST_LENGTH) {
 		err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
 		goto done;
 	}
-	SHA1Update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
+	got_hash_update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
 
 	err = read_fileindex_val32(&ie->flags, ctx, infile);
 	if (err)
@@ -684,7 +685,8 @@ read_fileindex_entry(struct got_fileindex_entry **iep,
 				err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
 				goto done;
 			}
-			SHA1Update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
+			got_hash_update(ctx, ie->staged_blob_sha1,
+			    SHA1_DIGEST_LENGTH);
 		}
 	} else {
 		/* GOT_FILE_INDEX_VERSION 1 does not support staging. */
@@ -704,14 +706,14 @@ got_fileindex_read(struct got_fileindex *fileindex, FI
 {
 	const struct got_error *err = NULL;
 	struct got_fileindex_hdr hdr;
-	SHA1_CTX ctx;
+	struct got_hash ctx;
 	struct got_fileindex_entry *ie;
 	uint8_t sha1_expected[SHA1_DIGEST_LENGTH];
 	uint8_t sha1[SHA1_DIGEST_LENGTH];
 	size_t n;
 	int i;
 
-	SHA1Init(&ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 	n = fread(&hdr.signature, 1, sizeof(hdr.signature), infile);
 	if (n != sizeof(hdr.signature)) {
@@ -732,9 +734,9 @@ got_fileindex_read(struct got_fileindex *fileindex, FI
 		return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
 	}
 
-	SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
-	SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
-	SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
+	got_hash_update(&ctx, &hdr.signature, sizeof(hdr.signature));
+	got_hash_update(&ctx, &hdr.version, sizeof(hdr.version));
+	got_hash_update(&ctx, &hdr.nentries, sizeof(hdr.nentries));
 
 	hdr.signature = be32toh(hdr.signature);
 	hdr.version = be32toh(hdr.version);
@@ -757,7 +759,7 @@ got_fileindex_read(struct got_fileindex *fileindex, FI
 	n = fread(sha1_expected, 1, sizeof(sha1_expected), infile);
 	if (n != sizeof(sha1_expected))
 		return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
-	SHA1Final(sha1, &ctx);
+	got_hash_final(&ctx, sha1);
 	if (memcmp(sha1, sha1_expected, SHA1_DIGEST_LENGTH) != 0)
 		return got_error(GOT_ERR_FILEIDX_CSUM);
 
blob - 2575eb63d3f9bfe0cd35903ae30824124fa07ca8
file + lib/got_lib_deflate.h
--- lib/got_lib_deflate.h
+++ lib/got_lib_deflate.h
@@ -20,6 +20,9 @@ struct got_deflate_checksum {
 
 	/* If not NULL, mix output bytes into this SHA1 context. */
 	SHA1_CTX *output_sha1;
+
+	/* If not NULL, mix output bytes into this hash context. */
+	struct got_hash *output_ctx;
 };
 
 struct got_deflate_buf {
blob - 6cf096112b33a6a0627c71d31836cc10bedb3469
file + lib/got_lib_hash.h
--- lib/got_lib_hash.h
+++ lib/got_lib_hash.h
@@ -17,6 +17,8 @@ int got_parse_xdigit(uint8_t *, const char *);
 #define GOT_SHA1_STRING_ZERO "0000000000000000000000000000000000000000"
 #define GOT_SHA256_STRING_ZERO "0000000000000000000000000000000000000000000000000000000000000000"
 
+#define GOT_HASH_DIGEST_MAXLEN SHA256_DIGEST_LENGTH
+
 int got_parse_xdigit(uint8_t *, const char *);
 
 char *got_sha1_digest_to_str(const uint8_t *, char *, size_t);
@@ -25,3 +27,26 @@ int got_parse_object_id(struct got_object_id *, const 
 int got_parse_hash_digest(uint8_t *, const char *, enum got_hash_algorithm);
 int got_parse_object_id(struct got_object_id *, const char *,
     enum got_hash_algorithm);
+
+struct got_hash {
+	SHA1_CTX		 sha1_ctx;
+	SHA2_CTX		 sha256_ctx;
+	enum got_hash_algorithm	 algo;
+};
+
+/*
+ * These functions allow to compute and check hashes.
+ * The hash function used is specified during got_hash_init.
+ * Data can be added with got_hash_update and, once done, the checksum
+ * saved in a buffer long at least GOT_HASH_DIGEST_MAXLEN bytes with
+ * got_hash_final or in an got_object_id with got_hash_final_object_id.
+ */
+void	got_hash_init(struct got_hash *, enum got_hash_algorithm);
+void	got_hash_update(struct got_hash *, const void *, size_t);
+void	got_hash_final(struct got_hash *, uint8_t *);
+void	got_hash_final_object_id(struct got_hash *, struct got_object_id *);
+
+/*
+ * Compare two hash digest; similar to memcmp().
+ */
+int	got_hash_cmp(enum got_hash_algorithm, uint8_t *, uint8_t *);
blob - 4ddc5aac63e46a5562147f8b561c4dea76cad65c
file + lib/got_lib_inflate.h
--- lib/got_lib_inflate.h
+++ lib/got_lib_inflate.h
@@ -21,11 +21,17 @@ struct got_inflate_checksum {
 	/* If not NULL, mix input bytes into this SHA1 context. */
 	SHA1_CTX *input_sha1;
 
+	/* if not NULL, mix input bytes into this hash context. */
+	struct got_hash *input_ctx;
+
 	/* If not NULL, mix output bytes into this CRC checksum. */
 	uint32_t *output_crc;
 
 	/* If not NULL, mix output bytes into this SHA1 context. */
 	SHA1_CTX *output_sha1;
+
+	/* If not NULL, mix output bytes into this hash context. */
+	struct got_hash *output_ctx;
 };
 
 struct got_inflate_buf {
blob - bec94759fda7eb63db8c6a38520291cd5c1a8ad1
file + lib/got_lib_pack_index.h
--- lib/got_lib_pack_index.h
+++ lib/got_lib_pack_index.h
@@ -18,7 +18,7 @@ const struct got_error *got_pack_hwrite(int, void *, i
     uint32_t nobj_total, uint32_t nobj_indexed, uint32_t nobj_loose,
     uint32_t nobj_resolved);
 
-const struct got_error *got_pack_hwrite(int, void *, int, SHA1_CTX *);
+const struct got_error *got_pack_hwrite(int, void *, int, struct got_hash *);
 
 const struct got_error *
 got_pack_index(struct got_pack *pack, int idxfd,
blob - 51a9a0ca1ab1e8d6bc5e9f6722d0595f2a3b5fe7
file + lib/hash.c
--- lib/hash.c
+++ lib/hash.c
@@ -128,3 +128,50 @@ got_parse_object_id(struct got_object_id *id, const ch
 
 	return got_parse_hash_digest(id->sha1, line, algo);
 }
+
+void
+got_hash_init(struct got_hash *hash, enum got_hash_algorithm algo)
+{
+	memset(hash, 0, sizeof(*hash));
+	hash->algo = algo;
+
+	if (algo == GOT_HASH_SHA1)
+		SHA1Init(&hash->sha1_ctx);
+	else if (algo == GOT_HASH_SHA256)
+		SHA256Init(&hash->sha256_ctx);
+}
+
+void
+got_hash_update(struct got_hash *hash, const void *data, size_t len)
+{
+	if (hash->algo == GOT_HASH_SHA1)
+		SHA1Update(&hash->sha1_ctx, data, len);
+	else if (hash->algo == GOT_HASH_SHA256)
+		SHA256Update(&hash->sha256_ctx, data, len);
+}
+
+void
+got_hash_final(struct got_hash *hash, uint8_t *out)
+{
+	if (hash->algo == GOT_HASH_SHA1)
+		SHA1Final(out, &hash->sha1_ctx);
+	else if (hash->algo == GOT_HASH_SHA256)
+		SHA256Final(out, &hash->sha256_ctx);
+}
+
+void
+got_hash_final_object_id(struct got_hash *hash, struct got_object_id *id)
+{
+	memset(id, 0, sizeof(*id));
+	got_hash_final(hash, id->sha1);
+}
+
+int
+got_hash_cmp(enum got_hash_algorithm algo, uint8_t *b1, uint8_t *b2)
+{
+	if (algo == GOT_HASH_SHA1)
+		return memcmp(b1, b2, SHA1_DIGEST_LENGTH);
+	else if (algo == GOT_HASH_SHA256)
+		return memcmp(b1, b2, SHA256_DIGEST_LENGTH);
+	return -1;
+}
blob - a7430b2862ad5a27a62c256b504986391d92854d
file + lib/inflate.c
--- lib/inflate.c
+++ lib/inflate.c
@@ -31,6 +31,7 @@
 #include "got_object.h"
 #include "got_path.h"
 
+#include "got_lib_hash.h"
 #include "got_lib_inflate.h"
 #include "got_lib_poll.h"
 
@@ -94,6 +95,9 @@ csum_input(struct got_inflate_checksum *csum, const ui
 
 	if (csum->input_sha1)
 		SHA1Update(csum->input_sha1, buf, len);
+
+	if (csum->input_ctx)
+		got_hash_update(csum->input_ctx, buf, len);
 }
 
 static void
@@ -104,6 +108,9 @@ csum_output(struct got_inflate_checksum *csum, const u
 
 	if (csum->output_sha1)
 		SHA1Update(csum->output_sha1, buf, len);
+
+	if (csum->output_ctx)
+		got_hash_update(csum->output_ctx, buf, len);
 }
 
 const struct got_error *
blob - d703aefd6b522073c9b2c3c3eb7f2cff2a4e3590
file + lib/object_create.c
--- lib/object_create.c
+++ lib/object_create.c
@@ -126,14 +126,14 @@ got_object_blob_file_create(struct got_object_id **id,
 	char *header = NULL;
 	int fd = -1;
 	struct stat sb;
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	size_t headerlen = 0, n;
 
 	*id = NULL;
 	*blobfile = NULL;
 	*blobsize = 0;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 	fd = open(ondisk_path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
 	if (fd == -1) {
@@ -155,7 +155,7 @@ got_object_blob_file_create(struct got_object_id **id,
 		goto done;
 	}
 	headerlen = strlen(header) + 1;
-	SHA1Update(&sha1_ctx, header, headerlen);
+	got_hash_update(&ctx, header, headerlen);
 
 	*blobfile = got_opentemp();
 	if (*blobfile == NULL) {
@@ -188,7 +188,7 @@ got_object_blob_file_create(struct got_object_id **id,
 		}
 		if (inlen == 0)
 			break; /* EOF */
-		SHA1Update(&sha1_ctx, buf, inlen);
+		got_hash_update(&ctx, buf, inlen);
 		n = fwrite(buf, 1, inlen, *blobfile);
 		if (n != inlen) {
 			err = got_ferror(*blobfile, GOT_ERR_IO);
@@ -204,7 +204,7 @@ got_object_blob_file_create(struct got_object_id **id,
 		err = got_error_from_errno("calloc");
 		goto done;
 	}
-	SHA1Final((*id)->sha1, &sha1_ctx);
+	got_hash_final_object_id(&ctx, *id);
 
 	if (fflush(*blobfile) != 0) {
 		err = got_error_from_errno("fflush");
@@ -307,7 +307,7 @@ got_object_tree_create(struct got_object_id **id,
 {
 	const struct got_error *err = NULL;
 	char modebuf[sizeof("100644 ")];
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	char *header = NULL;
 	size_t headerlen, len = 0, n;
 	FILE *treefile = NULL;
@@ -319,7 +319,7 @@ got_object_tree_create(struct got_object_id **id,
 
 	*id = NULL;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 	sorted_entries = calloc(nentries, sizeof(struct got_tree_entry *));
 	if (sorted_entries == NULL)
@@ -345,7 +345,7 @@ got_object_tree_create(struct got_object_id **id,
 		goto done;
 	}
 	headerlen = strlen(header) + 1;
-	SHA1Update(&sha1_ctx, header, headerlen);
+	got_hash_update(&ctx, header, headerlen);
 
 	treefile = got_opentemp();
 	if (treefile == NULL) {
@@ -371,7 +371,7 @@ got_object_tree_create(struct got_object_id **id,
 			err = got_ferror(treefile, GOT_ERR_IO);
 			goto done;
 		}
-		SHA1Update(&sha1_ctx, modebuf, len);
+		got_hash_update(&ctx, modebuf, len);
 		treesize += n;
 
 		len = strlen(te->name) + 1; /* must include NUL */
@@ -380,7 +380,7 @@ got_object_tree_create(struct got_object_id **id,
 			err = got_ferror(treefile, GOT_ERR_IO);
 			goto done;
 		}
-		SHA1Update(&sha1_ctx, te->name, len);
+		got_hash_update(&ctx, te->name, len);
 		treesize += n;
 
 		len = SHA1_DIGEST_LENGTH;
@@ -389,7 +389,7 @@ got_object_tree_create(struct got_object_id **id,
 			err = got_ferror(treefile, GOT_ERR_IO);
 			goto done;
 		}
-		SHA1Update(&sha1_ctx, te->id.sha1, len);
+		got_hash_update(&ctx, te->id.sha1, len);
 		treesize += n;
 	}
 
@@ -398,7 +398,7 @@ got_object_tree_create(struct got_object_id **id,
 		err = got_error_from_errno("calloc");
 		goto done;
 	}
-	SHA1Final((*id)->sha1, &sha1_ctx);
+	got_hash_final_object_id(&ctx, *id);
 
 	if (fflush(treefile) != 0) {
 		err = got_error_from_errno("fflush");
@@ -427,7 +427,7 @@ got_object_commit_create(struct got_object_id **id,
     const char *logmsg, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	char *header = NULL, *tree_str = NULL;
 	char *author_str = NULL, *committer_str = NULL;
 	char *id_str = NULL;
@@ -439,7 +439,7 @@ got_object_commit_create(struct got_object_id **id,
 
 	*id = NULL;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 	msg0 = strdup(logmsg);
 	if (msg0 == NULL)
@@ -478,7 +478,7 @@ got_object_commit_create(struct got_object_id **id,
 		goto done;
 	}
 	headerlen = strlen(header) + 1;
-	SHA1Update(&sha1_ctx, header, headerlen);
+	got_hash_update(&ctx, header, headerlen);
 
 	commitfile = got_opentemp();
 	if (commitfile == NULL) {
@@ -502,7 +502,7 @@ got_object_commit_create(struct got_object_id **id,
 		goto done;
 	}
 	len = strlen(tree_str);
-	SHA1Update(&sha1_ctx, tree_str, len);
+	got_hash_update(&ctx, tree_str, len);
 	n = fwrite(tree_str, 1, len, commitfile);
 	if (n != len) {
 		err = got_ferror(commitfile, GOT_ERR_IO);
@@ -525,7 +525,7 @@ got_object_commit_create(struct got_object_id **id,
 				goto done;
 			}
 			len = strlen(parent_str);
-			SHA1Update(&sha1_ctx, parent_str, len);
+			got_hash_update(&ctx, parent_str, len);
 			n = fwrite(parent_str, 1, len, commitfile);
 			if (n != len) {
 				err = got_ferror(commitfile, GOT_ERR_IO);
@@ -540,7 +540,7 @@ got_object_commit_create(struct got_object_id **id,
 	}
 
 	len = strlen(author_str);
-	SHA1Update(&sha1_ctx, author_str, len);
+	got_hash_update(&ctx, author_str, len);
 	n = fwrite(author_str, 1, len, commitfile);
 	if (n != len) {
 		err = got_ferror(commitfile, GOT_ERR_IO);
@@ -549,7 +549,7 @@ got_object_commit_create(struct got_object_id **id,
 	commitsize += n;
 
 	len = strlen(committer_str);
-	SHA1Update(&sha1_ctx, committer_str, len);
+	got_hash_update(&ctx, committer_str, len);
 	n = fwrite(committer_str, 1, len, commitfile);
 	if (n != len) {
 		err = got_ferror(commitfile, GOT_ERR_IO);
@@ -557,7 +557,7 @@ got_object_commit_create(struct got_object_id **id,
 	}
 	commitsize += n;
 
-	SHA1Update(&sha1_ctx, "\n", 1);
+	got_hash_update(&ctx, "\n", 1);
 	n = fwrite("\n", 1, 1, commitfile);
 	if (n != 1) {
 		err = got_ferror(commitfile, GOT_ERR_IO);
@@ -566,7 +566,7 @@ got_object_commit_create(struct got_object_id **id,
 	commitsize += n;
 
 	len = strlen(msg);
-	SHA1Update(&sha1_ctx, msg, len);
+	got_hash_update(&ctx, msg, len);
 	n = fwrite(msg, 1, len, commitfile);
 	if (n != len) {
 		err = got_ferror(commitfile, GOT_ERR_IO);
@@ -574,7 +574,7 @@ got_object_commit_create(struct got_object_id **id,
 	}
 	commitsize += n;
 
-	SHA1Update(&sha1_ctx, "\n", 1);
+	got_hash_update(&ctx, "\n", 1);
 	n = fwrite("\n", 1, 1, commitfile);
 	if (n != 1) {
 		err = got_ferror(commitfile, GOT_ERR_IO);
@@ -587,7 +587,7 @@ got_object_commit_create(struct got_object_id **id,
 		err = got_error_from_errno("calloc");
 		goto done;
 	}
-	SHA1Final((*id)->sha1, &sha1_ctx);
+	got_hash_final_object_id(&ctx, *id);
 
 	if (fflush(commitfile) != 0) {
 		err = got_error_from_errno("fflush");
@@ -619,7 +619,7 @@ got_object_tag_create(struct got_object_id **id,
     struct got_repository *repo, int verbosity)
 {
 	const struct got_error *err = NULL;
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	char *header = NULL;
 	char *tag_str = NULL, *tagger_str = NULL;
 	char *id_str = NULL, *obj_str = NULL, *type_str = NULL;
@@ -633,7 +633,7 @@ got_object_tag_create(struct got_object_id **id,
 
 	*id = NULL;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 	err = got_object_id_str(&id_str, object_id);
 	if (err)
@@ -764,7 +764,7 @@ got_object_tag_create(struct got_object_id **id,
 	}
 
 	headerlen = strlen(header) + 1;
-	SHA1Update(&sha1_ctx, header, headerlen);
+	got_hash_update(&ctx, header, headerlen);
 
 	tagfile = got_opentemp();
 	if (tagfile == NULL) {
@@ -779,7 +779,7 @@ got_object_tag_create(struct got_object_id **id,
 	}
 	tagsize += headerlen;
 	len = strlen(obj_str);
-	SHA1Update(&sha1_ctx, obj_str, len);
+	got_hash_update(&ctx, obj_str, len);
 	n = fwrite(obj_str, 1, len, tagfile);
 	if (n != len) {
 		err = got_ferror(tagfile, GOT_ERR_IO);
@@ -787,7 +787,7 @@ got_object_tag_create(struct got_object_id **id,
 	}
 	tagsize += n;
 	len = strlen(type_str);
-	SHA1Update(&sha1_ctx, type_str, len);
+	got_hash_update(&ctx, type_str, len);
 	n = fwrite(type_str, 1, len, tagfile);
 	if (n != len) {
 		err = got_ferror(tagfile, GOT_ERR_IO);
@@ -796,7 +796,7 @@ got_object_tag_create(struct got_object_id **id,
 	tagsize += n;
 
 	len = strlen(tag_str);
-	SHA1Update(&sha1_ctx, tag_str, len);
+	got_hash_update(&ctx, tag_str, len);
 	n = fwrite(tag_str, 1, len, tagfile);
 	if (n != len) {
 		err = got_ferror(tagfile, GOT_ERR_IO);
@@ -805,7 +805,7 @@ got_object_tag_create(struct got_object_id **id,
 	tagsize += n;
 
 	len = strlen(tagger_str);
-	SHA1Update(&sha1_ctx, tagger_str, len);
+	got_hash_update(&ctx, tagger_str, len);
 	n = fwrite(tagger_str, 1, len, tagfile);
 	if (n != len) {
 		err = got_ferror(tagfile, GOT_ERR_IO);
@@ -813,7 +813,7 @@ got_object_tag_create(struct got_object_id **id,
 	}
 	tagsize += n;
 
-	SHA1Update(&sha1_ctx, "\n", 1);
+	got_hash_update(&ctx, "\n", 1);
 	n = fwrite("\n", 1, 1, tagfile);
 	if (n != 1) {
 		err = got_ferror(tagfile, GOT_ERR_IO);
@@ -822,7 +822,7 @@ got_object_tag_create(struct got_object_id **id,
 	tagsize += n;
 
 	len = strlen(msg);
-	SHA1Update(&sha1_ctx, msg, len);
+	got_hash_update(&ctx, msg, len);
 	n = fwrite(msg, 1, len, tagfile);
 	if (n != len) {
 		err = got_ferror(tagfile, GOT_ERR_IO);
@@ -830,7 +830,7 @@ got_object_tag_create(struct got_object_id **id,
 	}
 	tagsize += n;
 
-	SHA1Update(&sha1_ctx, "\n", 1);
+	got_hash_update(&ctx, "\n", 1);
 	n = fwrite("\n", 1, 1, tagfile);
 	if (n != 1) {
 		err = got_ferror(tagfile, GOT_ERR_IO);
@@ -840,7 +840,7 @@ got_object_tag_create(struct got_object_id **id,
 
 	if (signer_id && buf_len(buf) > 0) {
 		len = buf_len(buf);
-		SHA1Update(&sha1_ctx, buf_get(buf), len);
+		got_hash_update(&ctx, buf_get(buf), len);
 		n = fwrite(buf_get(buf), 1, len, tagfile);
 		if (n != len) {
 			err = got_ferror(tagfile, GOT_ERR_IO);
@@ -854,7 +854,7 @@ got_object_tag_create(struct got_object_id **id,
 		err = got_error_from_errno("calloc");
 		goto done;
 	}
-	SHA1Final((*id)->sha1, &sha1_ctx);
+	got_hash_final_object_id(&ctx, *id);
 
 	if (fflush(tagfile) != 0) {
 		err = got_error_from_errno("fflush");
blob - 5efb32c22767820707ae2c11266b810d15999dfa
file + lib/object_parse.c
--- lib/object_parse.c
+++ lib/object_parse.c
@@ -290,8 +290,8 @@ got_object_read_raw(uint8_t **outbuf, off_t *size, siz
 	const struct got_error *err = NULL;
 	struct got_object *obj;
 	struct got_inflate_checksum csum;
-	uint8_t sha1[SHA1_DIGEST_LENGTH];
-	SHA1_CTX sha1_ctx;
+	struct got_object_id id;
+	struct got_hash ctx;
 	size_t len, consumed;
 	FILE *f = NULL;
 
@@ -299,9 +299,9 @@ got_object_read_raw(uint8_t **outbuf, off_t *size, siz
 	*size = 0;
 	*hdrlen = 0;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 	memset(&csum, 0, sizeof(csum));
-	csum.output_sha1 = &sha1_ctx;
+	csum.output_ctx = &ctx;
 
 	if (lseek(infd, SEEK_SET, 0) == -1)
 		return got_error_from_errno("lseek");
@@ -342,8 +342,8 @@ got_object_read_raw(uint8_t **outbuf, off_t *size, siz
 		goto done;
 	}
 
-	SHA1Final(sha1, &sha1_ctx);
-	if (memcmp(expected_id->sha1, sha1, SHA1_DIGEST_LENGTH) != 0) {
+	got_hash_final_object_id(&ctx, &id);
+	if (got_object_id_cmp(expected_id, &id) != 0) {
 		err = got_error_checksum(expected_id);
 		goto done;
 	}
@@ -751,18 +751,18 @@ got_object_read_commit(struct got_commit_object **comm
 	size_t len;
 	uint8_t *p;
 	struct got_inflate_checksum csum;
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	struct got_object_id id;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 	memset(&csum, 0, sizeof(csum));
-	csum.output_sha1 = &sha1_ctx;
+	csum.output_ctx = &ctx;
 
 	err = got_inflate_to_mem_fd(&p, &len, NULL, &csum, expected_size, fd);
 	if (err)
 		return err;
 
-	SHA1Final(id.sha1, &sha1_ctx);
+	got_hash_final_object_id(&ctx, &id);
 	if (got_object_id_cmp(expected_id, &id) != 0) {
 		err = got_error_checksum(expected_id);
 		goto done;
@@ -920,18 +920,18 @@ got_object_read_tree(struct got_parsed_tree_entry **en
 	struct got_object *obj = NULL;
 	size_t len;
 	struct got_inflate_checksum csum;
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	struct got_object_id id;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 	memset(&csum, 0, sizeof(csum));
-	csum.output_sha1 = &sha1_ctx;
+	csum.output_ctx = &ctx;
 
 	err = got_inflate_to_mem_fd(p, &len, NULL, &csum, 0, fd);
 	if (err)
 		return err;
 
-	SHA1Final(id.sha1, &sha1_ctx);
+	got_hash_final_object_id(&ctx, &id);
 	if (got_object_id_cmp(expected_id, &id) != 0) {
 		err = got_error_checksum(expected_id);
 		goto done;
@@ -1160,19 +1160,19 @@ got_object_read_tag(struct got_tag_object **tag, int f
 	size_t len;
 	uint8_t *p;
 	struct got_inflate_checksum csum;
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	struct got_object_id id;
 
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 	memset(&csum, 0, sizeof(csum));
-	csum.output_sha1 = &sha1_ctx;
+	csum.output_ctx = &ctx;
 
 	err = got_inflate_to_mem_fd(&p, &len, NULL, &csum,
 	    expected_size, fd);
 	if (err)
 		return err;
 
-	SHA1Final(id.sha1, &sha1_ctx);
+	got_hash_final_object_id(&ctx, &id);
 	if (got_object_id_cmp(expected_id, &id) != 0) {
 		err = got_error_checksum(expected_id);
 		goto done;
blob - 41355efc540436f1b9cbb6f193ac0916c8dffb06
file + lib/pack.c
--- lib/pack.c
+++ lib/pack.c
@@ -74,14 +74,15 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 got_packidx_init_hdr(struct got_packidx *p, int verify, off_t packfile_size)
 {
 	const struct got_error *err = NULL;
+	enum got_hash_algorithm algo = GOT_HASH_SHA1;
 	struct got_packidx_v2_hdr *h;
-	SHA1_CTX ctx;
-	uint8_t sha1[SHA1_DIGEST_LENGTH];
+	struct got_hash ctx;
+	uint8_t hash[GOT_HASH_DIGEST_MAXLEN];
 	size_t nobj, len_fanout, len_ids, offset, remain;
 	ssize_t n;
 	int i;
 
-	SHA1Init(&ctx);
+	got_hash_init(&ctx, algo);
 
 	h = &p->hdr;
 	offset = 0;
@@ -116,7 +117,7 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 	remain -= sizeof(*h->magic);
 
 	if (verify)
-		SHA1Update(&ctx, (uint8_t *)h->magic, sizeof(*h->magic));
+		got_hash_update(&ctx, h->magic, sizeof(*h->magic));
 
 	if (remain < sizeof(*h->version)) {
 		err = got_error(GOT_ERR_BAD_PACKIDX);
@@ -147,7 +148,7 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 	remain -= sizeof(*h->version);
 
 	if (verify)
-		SHA1Update(&ctx, (uint8_t *)h->version, sizeof(*h->version));
+		got_hash_update(&ctx, h->version, sizeof(*h->version));
 
 	len_fanout =
 	    sizeof(*h->fanout_table) * GOT_PACKIDX_V2_FANOUT_TABLE_ITEMS;
@@ -176,7 +177,7 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 	if (err)
 		goto done;
 	if (verify)
-		SHA1Update(&ctx, (uint8_t *)h->fanout_table, len_fanout);
+		got_hash_update(&ctx, h->fanout_table, len_fanout);
 	offset += len_fanout;
 	remain -= len_fanout;
 
@@ -204,7 +205,7 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 		}
 	}
 	if (verify)
-		SHA1Update(&ctx, (uint8_t *)h->sorted_ids, len_ids);
+		got_hash_update(&ctx, h->sorted_ids, len_ids);
 	offset += len_ids;
 	remain -= len_ids;
 
@@ -229,7 +230,7 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 		}
 	}
 	if (verify)
-		SHA1Update(&ctx, (uint8_t *)h->crc32, nobj * sizeof(*h->crc32));
+		got_hash_update(&ctx, h->crc32, nobj * sizeof(*h->crc32));
 	remain -= nobj * sizeof(*h->crc32);
 	offset += nobj * sizeof(*h->crc32);
 
@@ -254,8 +255,7 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 		}
 	}
 	if (verify)
-		SHA1Update(&ctx, (uint8_t *)h->offsets,
-		    nobj * sizeof(*h->offsets));
+		got_hash_update(&ctx, h->offsets, nobj * sizeof(*h->offsets));
 	remain -= nobj * sizeof(*h->offsets);
 	offset += nobj * sizeof(*h->offsets);
 
@@ -297,7 +297,7 @@ got_packidx_init_hdr(struct got_packidx *p, int verify
 		}
 	}
 	if (verify)
-		SHA1Update(&ctx, (uint8_t*)h->large_offsets,
+		got_hash_update(&ctx, h->large_offsets,
 		    p->nlargeobj * sizeof(*h->large_offsets));
 	remain -= p->nlargeobj * sizeof(*h->large_offsets);
 	offset += p->nlargeobj * sizeof(*h->large_offsets);
@@ -325,10 +325,11 @@ checksum:
 		}
 	}
 	if (verify) {
-		SHA1Update(&ctx, h->trailer->packfile_sha1, SHA1_DIGEST_LENGTH);
-		SHA1Final(sha1, &ctx);
-		if (memcmp(h->trailer->packidx_sha1, sha1,
-		    SHA1_DIGEST_LENGTH) != 0)
+		got_hash_update(&ctx, h->trailer->packfile_sha1,
+		    SHA1_DIGEST_LENGTH);
+		got_hash_final(&ctx, hash);
+		if (got_hash_cmp(ctx.algo, hash, h->trailer->packidx_sha1)
+		    != 0)
 			err = got_error(GOT_ERR_PACKIDX_CSUM);
 	}
 done:
blob - da928cb0a1c732e0c7d6f0df0401177528771e3f
file + lib/pack_create.c
--- lib/pack_create.c
+++ lib/pack_create.c
@@ -48,6 +48,7 @@
 
 #include "got_lib_deltify.h"
 #include "got_lib_delta.h"
+#include "got_lib_hash.h"
 #include "got_lib_object.h"
 #include "got_lib_object_idset.h"
 #include "got_lib_object_cache.h"
@@ -1420,14 +1421,14 @@ hwrite(int fd, const void *buf, off_t len, SHA1_CTX *c
 }
 
 static const struct got_error *
-hwrite(int fd, const void *buf, off_t len, SHA1_CTX *ctx)
+hwrite(int fd, const void *buf, off_t len, struct got_hash *ctx)
 {
-	SHA1Update(ctx, buf, len);
+	got_hash_update(ctx, buf, len);
 	return got_poll_write_full(fd, buf, len);
 }
 
 static const struct got_error *
-hcopy(FILE *fsrc, int fd_dst, off_t len, SHA1_CTX *ctx)
+hcopy(FILE *fsrc, int fd_dst, off_t len, struct got_hash *ctx)
 {
 	const struct got_error *err;
 	unsigned char buf[65536];
@@ -1439,7 +1440,7 @@ hcopy(FILE *fsrc, int fd_dst, off_t len, SHA1_CTX *ctx
 		n = fread(buf, 1, copylen, fsrc);
 		if (n != copylen)
 			return got_ferror(fsrc, GOT_ERR_IO);
-		SHA1Update(ctx, buf, copylen);
+		got_hash_update(ctx, buf, copylen);
 		err = got_poll_write_full(fd_dst, buf, copylen);
 		if (err)
 			return err;
@@ -1451,12 +1452,12 @@ hcopy_mmap(uint8_t *src, off_t src_offset, size_t src_
 
 static const struct got_error *
 hcopy_mmap(uint8_t *src, off_t src_offset, size_t src_size,
-    int fd, off_t len, SHA1_CTX *ctx)
+    int fd, off_t len, struct got_hash *ctx)
 {
 	if (src_offset + len > src_size)
 		return got_error(GOT_ERR_RANGE);
 
-	SHA1Update(ctx, src + src_offset, len);
+	got_hash_update(ctx, src + src_offset, len);
 	return got_poll_write_full(fd, src + src_offset, len);
 }
 
@@ -1551,8 +1552,8 @@ deltahdr(off_t *packfile_size, SHA1_CTX *ctx, int pack
 }
 
 static const struct got_error *
-deltahdr(off_t *packfile_size, SHA1_CTX *ctx, int packfd, int force_refdelta,
-    struct got_pack_meta *m)
+deltahdr(off_t *packfile_size, struct got_hash *ctx, int packfd,
+    int force_refdelta, struct got_pack_meta *m)
 {
 	const struct got_error *err;
 	char buf[32];
@@ -1590,7 +1591,7 @@ write_packed_object(off_t *packfile_size, int packfd,
 static const struct got_error *
 write_packed_object(off_t *packfile_size, int packfd,
     FILE *delta_cache, uint8_t *delta_cache_map, size_t delta_cache_size,
-    struct got_pack_meta *m, int *outfd, SHA1_CTX *ctx,
+    struct got_pack_meta *m, int *outfd, struct got_hash *ctx,
     struct got_repository *repo, int force_refdelta)
 {
 	const struct got_error *err = NULL;
@@ -1600,7 +1601,7 @@ write_packed_object(off_t *packfile_size, int packfd,
 	struct got_raw_object *raw = NULL;
 	off_t outlen, delta_offset;
 
-	csum.output_sha1 = ctx;
+	csum.output_ctx = ctx;
 	csum.output_crc = NULL;
 
 	if (m->reused_delta_offset)
@@ -1694,7 +1695,7 @@ genpack(uint8_t *pack_sha1, int packfd, struct got_pac
 {
 	const struct got_error *err = NULL;
 	int i;
-	SHA1_CTX ctx;
+	struct got_hash ctx;
 	struct got_pack_meta *m;
 	char buf[32];
 	off_t packfile_size = 0;
@@ -1704,7 +1705,7 @@ genpack(uint8_t *pack_sha1, int packfd, struct got_pac
 	size_t delta_cache_size = 0;
 	FILE *packfile = NULL;
 
-	SHA1Init(&ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 
 #ifndef GOT_PACK_NO_MMAP
 	delta_cache_fd = dup(fileno(delta_cache));
@@ -1785,7 +1786,7 @@ genpack(uint8_t *pack_sha1, int packfd, struct got_pac
 			goto done;
 	}
 
-	SHA1Final(pack_sha1, &ctx);
+	got_hash_final(&ctx, pack_sha1);
 	err = got_poll_write_full(packfd, pack_sha1, SHA1_DIGEST_LENGTH);
 	if (err)
 		goto done;
blob - d7107177f8056953e03aac01278c49f2aee6f81f
file + lib/pack_index.c
--- lib/pack_index.c
+++ lib/pack_index.c
@@ -130,7 +130,7 @@ read_checksum(uint32_t *crc, SHA1_CTX *sha1_ctx, int f
 }
 
 static const struct got_error *
-read_checksum(uint32_t *crc, SHA1_CTX *sha1_ctx, int fd, size_t len)
+read_checksum(uint32_t *crc, struct got_hash *ctx, int fd, size_t len)
 {
 	uint8_t buf[8192];
 	size_t n;
@@ -144,15 +144,15 @@ read_checksum(uint32_t *crc, SHA1_CTX *sha1_ctx, int f
 			break;
 		if (crc)
 			*crc = crc32(*crc, buf, r);
-		if (sha1_ctx)
-			SHA1Update(sha1_ctx, buf, r);
+		if (ctx)
+			got_hash_update(ctx, buf, r);
 	}
 
 	return NULL;
 }
 
 static const struct got_error *
-read_file_sha1(SHA1_CTX *ctx, FILE *f, size_t len)
+read_file_digest(struct got_hash *ctx, FILE *f, size_t len)
 {
 	uint8_t buf[8192];
 	size_t n, r;
@@ -164,7 +164,7 @@ read_file_sha1(SHA1_CTX *ctx, FILE *f, size_t len)
 				return NULL;
 			return got_ferror(f, GOT_ERR_IO);
 		}
-		SHA1Update(ctx, buf, r);
+		got_hash_update(ctx, buf, r);
 	}
 
 	return NULL;
@@ -172,10 +172,10 @@ read_packed_object(struct got_pack *pack, struct got_i
 
 static const struct got_error *
 read_packed_object(struct got_pack *pack, struct got_indexed_object *obj,
-    FILE *tmpfile, SHA1_CTX *pack_sha1_ctx)
+    FILE *tmpfile, struct got_hash *pack_sha1_ctx)
 {
 	const struct got_error *err = NULL;
-	SHA1_CTX ctx;
+	struct got_hash ctx;
 	uint8_t *data = NULL;
 	size_t datalen = 0;
 	ssize_t n;
@@ -186,7 +186,7 @@ read_packed_object(struct got_pack *pack, struct got_i
 	struct got_inflate_checksum csum;
 
 	memset(&csum, 0, sizeof(csum));
-	csum.input_sha1 = pack_sha1_ctx;
+	csum.input_ctx = pack_sha1_ctx;
 	csum.input_crc = &obj->crc;
 
 	err = got_pack_parse_object_type_and_size(&obj->type, &obj->size,
@@ -196,7 +196,7 @@ read_packed_object(struct got_pack *pack, struct got_i
 
 	if (pack->map) {
 		obj->crc = crc32(obj->crc, pack->map + mapoff, obj->tslen);
-		SHA1Update(pack_sha1_ctx, pack->map + mapoff, obj->tslen);
+		got_hash_update(pack_sha1_ctx, pack->map + mapoff, obj->tslen);
 		mapoff += obj->tslen;
 	} else {
 		/* XXX Seek back and get the CRC of on-disk type+size bytes. */
@@ -238,7 +238,7 @@ read_packed_object(struct got_pack *pack, struct got_i
 		}
 		if (err)
 			break;
-		SHA1Init(&ctx);
+		got_hash_init(&ctx, GOT_HASH_SHA1);
 		err = get_obj_type_label(&obj_label, obj->type);
 		if (err) {
 			free(data);
@@ -251,17 +251,17 @@ read_packed_object(struct got_pack *pack, struct got_i
 			break;
 		}
 		headerlen = strlen(header) + 1;
-		SHA1Update(&ctx, header, headerlen);
+		got_hash_update(&ctx, header, headerlen);
 		if (obj->size > GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
-			err = read_file_sha1(&ctx, tmpfile, datalen);
+			err = read_file_digest(&ctx, tmpfile, datalen);
 			if (err) {
 				free(header);
 				free(data);
 				break;
 			}
 		} else
-			SHA1Update(&ctx, data, datalen);
-		SHA1Final(obj->id.sha1, &ctx);
+			got_hash_update(&ctx, data, datalen);
+		got_hash_final_object_id(&ctx, &obj->id);
 		free(header);
 		free(data);
 		break;
@@ -282,7 +282,7 @@ read_packed_object(struct got_pack *pack, struct got_i
 			    SHA1_DIGEST_LENGTH);
 			obj->crc = crc32(obj->crc, pack->map + mapoff,
 			    SHA1_DIGEST_LENGTH);
-			SHA1Update(pack_sha1_ctx, pack->map + mapoff,
+			got_hash_update(pack_sha1_ctx, pack->map + mapoff,
 			    SHA1_DIGEST_LENGTH);
 			mapoff += SHA1_DIGEST_LENGTH;
 			err = got_inflate_to_mem_mmap(NULL, &datalen,
@@ -303,8 +303,8 @@ read_packed_object(struct got_pack *pack, struct got_i
 			}
 			obj->crc = crc32(obj->crc, obj->delta.ref.ref_id.sha1,
 			    SHA1_DIGEST_LENGTH);
-			SHA1Update(pack_sha1_ctx, obj->delta.ref.ref_id.sha1,
-			    SHA1_DIGEST_LENGTH);
+			got_hash_update(pack_sha1_ctx,
+			    obj->delta.ref.ref_id.sha1, SHA1_DIGEST_LENGTH);
 			err = got_inflate_to_mem_fd(NULL, &datalen, &obj->len,
 			    &csum, obj->size, pack->fd);
 			if (err)
@@ -337,7 +337,7 @@ read_packed_object(struct got_pack *pack, struct got_i
 
 			obj->crc = crc32(obj->crc, pack->map + mapoff,
 			    obj->delta.ofs.base_offsetlen);
-			SHA1Update(pack_sha1_ctx, pack->map + mapoff,
+			got_hash_update(pack_sha1_ctx, pack->map + mapoff,
 			    obj->delta.ofs.base_offsetlen);
 			mapoff += obj->delta.ofs.base_offsetlen;
 			err = got_inflate_to_mem_mmap(NULL, &datalen,
@@ -376,11 +376,11 @@ got_pack_hwrite(int fd, void *buf, int len, SHA1_CTX *
 }
 
 const struct got_error *
-got_pack_hwrite(int fd, void *buf, int len, SHA1_CTX *ctx)
+got_pack_hwrite(int fd, void *buf, int len, struct got_hash *ctx)
 {
 	ssize_t w;
 
-	SHA1Update(ctx, buf, len);
+	got_hash_update(ctx, buf, len);
 
 	w = write(fd, buf, len);
 	if (w == -1)
@@ -401,7 +401,7 @@ resolve_deltified_object(struct got_pack *pack, struct
 	struct got_delta *delta;
 	uint8_t *buf = NULL;
 	size_t len = 0;
-	SHA1_CTX ctx;
+	struct got_hash ctx;
 	char *header = NULL;
 	size_t headerlen;
 	uint64_t max_size;
@@ -446,15 +446,15 @@ resolve_deltified_object(struct got_pack *pack, struct
 		goto done;
 	}
 	headerlen = strlen(header) + 1;
-	SHA1Init(&ctx);
-	SHA1Update(&ctx, header, headerlen);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
+	got_hash_update(&ctx, header, headerlen);
 	if (max_size > GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
-		err = read_file_sha1(&ctx, tmpfile, len);
+		err = read_file_digest(&ctx, tmpfile, len);
 		if (err)
 			goto done;
 	} else
-		SHA1Update(&ctx, buf, len);
-	SHA1Final(obj->id.sha1, &ctx);
+		got_hash_update(&ctx, buf, len);
+	got_hash_final_object_id(&ctx, &obj->id);
 done:
 	free(buf);
 	free(header);
@@ -639,7 +639,7 @@ got_pack_index(struct got_pack *pack, int idxfd, FILE 
 	char pack_sha1[SHA1_DIGEST_LENGTH];
 	uint32_t nobj, nvalid, nloose, nresolved = 0, i;
 	struct got_indexed_object *objects = NULL, *obj;
-	SHA1_CTX ctx;
+	struct got_hash ctx;
 	uint8_t packidx_hash[SHA1_DIGEST_LENGTH];
 	ssize_t r, w;
 	int pass, have_ref_deltas = 0, first_delta_idx = -1;
@@ -676,8 +676,8 @@ got_pack_index(struct got_pack *pack, int idxfd, FILE 
 		    "bad packfile with zero objects");
 
 	/* We compute the SHA1 of pack file contents and verify later on. */
-	SHA1Init(&ctx);
-	SHA1Update(&ctx, (void *)&hdr, sizeof(hdr));
+	got_hash_init(&ctx, GOT_HASH_SHA1);
+	got_hash_update(&ctx, &hdr, sizeof(hdr));
 
 	/*
 	 * Create an in-memory pack index which will grow as objects
@@ -800,7 +800,7 @@ got_pack_index(struct got_pack *pack, int idxfd, FILE 
 	 * Having done a full pass over the pack file and can now
 	 * verify its checksum.
 	 */
-	SHA1Final(pack_sha1, &ctx);
+	got_hash_final(&ctx, pack_sha1);
 
 	if (memcmp(pack_sha1_expected, pack_sha1, SHA1_DIGEST_LENGTH) != 0) {
 		err = got_error(GOT_ERR_PACKFILE_CSUM);
@@ -935,7 +935,7 @@ got_pack_index(struct got_pack *pack, int idxfd, FILE 
 	free(objects);
 	objects = NULL;
 
-	SHA1Init(&ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 	putbe32(buf, GOT_PACKIDX_V2_MAGIC);
 	putbe32(buf + 4, GOT_PACKIDX_VERSION);
 	err = got_pack_hwrite(idxfd, buf, 8, &ctx);
@@ -967,7 +967,7 @@ got_pack_index(struct got_pack *pack, int idxfd, FILE 
 	if (err)
 		goto done;
 
-	SHA1Final(packidx_hash, &ctx);
+	got_hash_final(&ctx, packidx_hash);
 	w = write(idxfd, packidx_hash, sizeof(packidx_hash));
 	if (w == -1) {
 		err = got_error_from_errno("write");
blob - 7d0e1dac2141bac1193dc36c27fc80b7cda09259
file + libexec/got-fetch-pack/got-fetch-pack.c
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
@@ -351,14 +351,14 @@ fetch_pack(int fd, int packfd, uint8_t *pack_sha1,
 	struct got_pathlist_entry *pe;
 	int sent_my_capabilites = 0, have_sidebands = 0;
 	int found_branch = 0;
-	SHA1_CTX sha1_ctx;
+	struct got_hash ctx;
 	uint8_t sha1_buf[SHA1_DIGEST_LENGTH];
 	size_t sha1_buf_len = 0;
 	ssize_t w;
 	struct got_ratelimit rl;
 
 	TAILQ_INIT(&symrefs);
-	SHA1Init(&sha1_ctx);
+	got_hash_init(&ctx, GOT_HASH_SHA1);
 	got_ratelimit_init(&rl, 0, 500);
 
 	have = malloc(refsz * sizeof(have[0]));
@@ -762,7 +762,8 @@ fetch_pack(int fd, int packfd, uint8_t *pack_sha1,
 				    sha1_buf_len + r > SHA1_DIGEST_LENGTH) {
 					size_t nshift = MIN(sha1_buf_len + r -
 					    SHA1_DIGEST_LENGTH, sha1_buf_len);
-					SHA1Update(&sha1_ctx, sha1_buf, nshift);
+					got_hash_update(&ctx, sha1_buf,
+					    nshift);
 					memmove(sha1_buf, sha1_buf + nshift,
 					    sha1_buf_len - nshift);
 					sha1_buf_len -= nshift;
@@ -776,7 +777,7 @@ fetch_pack(int fd, int packfd, uint8_t *pack_sha1,
 				 * Mix in previously buffered bytes which
 				 * are not part of the checksum after all.
 				 */
-				SHA1Update(&sha1_ctx, sha1_buf, r);
+				got_hash_update(&ctx, sha1_buf, r);
 
 				/* Update potential checksum buffer. */
 				memmove(sha1_buf, sha1_buf + r,
@@ -785,10 +786,10 @@ fetch_pack(int fd, int packfd, uint8_t *pack_sha1,
 			}
 		} else {
 			/* Mix in any previously buffered bytes. */
-			SHA1Update(&sha1_ctx, sha1_buf, sha1_buf_len);
+			got_hash_update(&ctx, sha1_buf, sha1_buf_len);
 
 			/* Mix in bytes read minus potential checksum bytes. */
-			SHA1Update(&sha1_ctx, buf, r - SHA1_DIGEST_LENGTH);
+			got_hash_update(&ctx, buf, r - SHA1_DIGEST_LENGTH);
 
 			/* Buffer potential checksum bytes. */
 			memcpy(sha1_buf, buf + r - SHA1_DIGEST_LENGTH,
@@ -820,7 +821,7 @@ fetch_pack(int fd, int packfd, uint8_t *pack_sha1,
 	if (err)
 		goto done;
 
-	SHA1Final(pack_sha1, &sha1_ctx);
+	got_hash_final(&ctx, pack_sha1);
 	if (sha1_buf_len != SHA1_DIGEST_LENGTH ||
 	    memcmp(pack_sha1, sha1_buf, sha1_buf_len) != 0) {
 		err = got_error_msg(GOT_ERR_BAD_PACKFILE,
blob - 6211bae909a86681a6c240bb9f1d8e44a471a89d
file + libexec/got-index-pack/got-index-pack.c
--- libexec/got-index-pack/got-index-pack.c
+++ libexec/got-index-pack/got-index-pack.c
@@ -36,6 +36,7 @@
 
 #include "got_lib_delta.h"
 #include "got_lib_delta_cache.h"
+#include "got_lib_hash.h"
 #include "got_lib_object.h"
 #include "got_lib_privsep.h"
 #include "got_lib_ratelimit.h"
blob - ed8291ef577fe09d4957c03653a00c031b0cb0dd
file + libexec/got-read-blob/got-read-blob.c
--- libexec/got-read-blob/got-read-blob.c
+++ libexec/got-read-blob/got-read-blob.c
@@ -35,6 +35,7 @@
 #include "got_object.h"
 
 #include "got_lib_delta.h"
+#include "got_lib_hash.h"
 #include "got_lib_inflate.h"
 #include "got_lib_object.h"
 #include "got_lib_object_parse.h"
@@ -77,11 +78,11 @@ main(int argc, char *argv[])
 		struct got_object_id id;
 		struct got_object_id expected_id;
 		struct got_inflate_checksum csum;
-		SHA1_CTX sha1_ctx;
+		struct got_hash ctx;
 
-		SHA1Init(&sha1_ctx);
+		got_hash_init(&ctx, GOT_HASH_SHA1);
 		memset(&csum, 0, sizeof(csum));
-		csum.output_sha1 = &sha1_ctx;
+		csum.output_ctx = &ctx;
 
 		memset(&imsg, 0, sizeof(imsg));
 		imsg.fd = -1;
@@ -170,7 +171,7 @@ main(int argc, char *argv[])
 			if (err)
 				goto done;
 		}
-		SHA1Final(id.sha1, &sha1_ctx);
+		got_hash_final_object_id(&ctx, &id);
 		if (got_object_id_cmp(&expected_id, &id) != 0) {
 			err = got_error_checksum(&expected_id);
 			goto done;