summaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/checksum.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/checksum.c')
-rw-r--r--subversion/libsvn_subr/checksum.c411
1 files changed, 360 insertions, 51 deletions
diff --git a/subversion/libsvn_subr/checksum.c b/subversion/libsvn_subr/checksum.c
index e5d6a62..6b195f8 100644
--- a/subversion/libsvn_subr/checksum.c
+++ b/subversion/libsvn_subr/checksum.c
@@ -21,6 +21,7 @@
* ====================================================================
*/
+#define APR_WANT_BYTEFUNC
#include <ctype.h>
@@ -30,9 +31,10 @@
#include "svn_checksum.h"
#include "svn_error.h"
#include "svn_ctype.h"
+#include "svn_sorts.h"
-#include "sha1.h"
-#include "md5.h"
+#include "checksum.h"
+#include "fnv1a.h"
#include "private/svn_subr_private.h"
@@ -40,17 +42,117 @@
+/* The MD5 digest for the empty string. */
+static const unsigned char md5_empty_string_digest_array[] = {
+ 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
+ 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
+};
+
+/* The SHA1 digest for the empty string. */
+static const unsigned char sha1_empty_string_digest_array[] = {
+ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
+ 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09
+};
+
+/* The FNV-1a digest for the empty string. */
+static const unsigned char fnv1a_32_empty_string_digest_array[] = {
+ 0x81, 0x1c, 0x9d, 0xc5
+};
+
+/* The FNV-1a digest for the empty string. */
+static const unsigned char fnv1a_32x4_empty_string_digest_array[] = {
+ 0xcd, 0x6d, 0x9a, 0x85
+};
+
+/* Digests for an empty string, indexed by checksum type */
+static const unsigned char * empty_string_digests[] = {
+ md5_empty_string_digest_array,
+ sha1_empty_string_digest_array,
+ fnv1a_32_empty_string_digest_array,
+ fnv1a_32x4_empty_string_digest_array
+};
+
+/* Digest sizes in bytes, indexed by checksum type */
+static const apr_size_t digest_sizes[] = {
+ APR_MD5_DIGESTSIZE,
+ APR_SHA1_DIGESTSIZE,
+ sizeof(apr_uint32_t),
+ sizeof(apr_uint32_t)
+};
+
+/* Checksum type prefixes used in serialized checksums. */
+static const char *ckind_str[] = {
+ "$md5 $",
+ "$sha1$",
+ "$fnv1$",
+ "$fnvm$",
+};
+
/* Returns the digest size of it's argument. */
-#define DIGESTSIZE(k) ((k) == svn_checksum_md5 ? APR_MD5_DIGESTSIZE : \
- (k) == svn_checksum_sha1 ? APR_SHA1_DIGESTSIZE : 0)
+#define DIGESTSIZE(k) \
+ (((k) < svn_checksum_md5 || (k) > svn_checksum_fnv1a_32x4) ? 0 : digest_sizes[k])
+
+/* Largest supported digest size */
+#define MAX_DIGESTSIZE (MAX(APR_MD5_DIGESTSIZE,APR_SHA1_DIGESTSIZE))
+
+const unsigned char *
+svn__empty_string_digest(svn_checksum_kind_t kind)
+{
+ return empty_string_digests[kind];
+}
+
+const char *
+svn__digest_to_cstring_display(const unsigned char digest[],
+ apr_size_t digest_size,
+ apr_pool_t *pool)
+{
+ static const char *hex = "0123456789abcdef";
+ char *str = apr_palloc(pool, (digest_size * 2) + 1);
+ apr_size_t i;
+
+ for (i = 0; i < digest_size; i++)
+ {
+ str[i*2] = hex[digest[i] >> 4];
+ str[i*2+1] = hex[digest[i] & 0x0f];
+ }
+ str[i*2] = '\0';
+
+ return str;
+}
+
+
+const char *
+svn__digest_to_cstring(const unsigned char digest[],
+ apr_size_t digest_size,
+ apr_pool_t *pool)
+{
+ static const unsigned char zeros_digest[MAX_DIGESTSIZE] = { 0 };
+
+ if (memcmp(digest, zeros_digest, digest_size) != 0)
+ return svn__digest_to_cstring_display(digest, digest_size, pool);
+ else
+ return NULL;
+}
+svn_boolean_t
+svn__digests_match(const unsigned char d1[],
+ const unsigned char d2[],
+ apr_size_t digest_size)
+{
+ static const unsigned char zeros[MAX_DIGESTSIZE] = { 0 };
+
+ return ((memcmp(d1, d2, digest_size) == 0)
+ || (memcmp(d2, zeros, digest_size) == 0)
+ || (memcmp(d1, zeros, digest_size) == 0));
+}
+
/* Check to see if KIND is something we recognize. If not, return
* SVN_ERR_BAD_CHECKSUM_KIND */
static svn_error_t *
validate_kind(svn_checksum_kind_t kind)
{
- if (kind == svn_checksum_md5 || kind == svn_checksum_sha1)
+ if (kind >= svn_checksum_md5 && kind <= svn_checksum_fnv1a_32x4)
return SVN_NO_ERROR;
else
return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
@@ -71,12 +173,15 @@ checksum_create_without_digest(svn_checksum_kind_t kind,
return checksum;
}
+/* Return a checksum object, allocated in POOL. The checksum will be of
+ * type KIND and contain the given DIGEST.
+ */
static svn_checksum_t *
checksum_create(svn_checksum_kind_t kind,
- apr_size_t digest_size,
const unsigned char *digest,
apr_pool_t *pool)
{
+ apr_size_t digest_size = DIGESTSIZE(kind);
svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size,
pool);
memcpy((unsigned char *)checksum->digest, digest, digest_size);
@@ -93,11 +198,12 @@ svn_checksum_create(svn_checksum_kind_t kind,
switch (kind)
{
case svn_checksum_md5:
- digest_size = APR_MD5_DIGESTSIZE;
- break;
case svn_checksum_sha1:
- digest_size = APR_SHA1_DIGESTSIZE;
+ case svn_checksum_fnv1a_32:
+ case svn_checksum_fnv1a_32x4:
+ digest_size = digest_sizes[kind];
break;
+
default:
return NULL;
}
@@ -111,16 +217,28 @@ svn_checksum_t *
svn_checksum__from_digest_md5(const unsigned char *digest,
apr_pool_t *result_pool)
{
- return checksum_create(svn_checksum_md5, APR_MD5_DIGESTSIZE, digest,
- result_pool);
+ return checksum_create(svn_checksum_md5, digest, result_pool);
}
svn_checksum_t *
svn_checksum__from_digest_sha1(const unsigned char *digest,
apr_pool_t *result_pool)
{
- return checksum_create(svn_checksum_sha1, APR_SHA1_DIGESTSIZE, digest,
- result_pool);
+ return checksum_create(svn_checksum_sha1, digest, result_pool);
+}
+
+svn_checksum_t *
+svn_checksum__from_digest_fnv1a_32(const unsigned char *digest,
+ apr_pool_t *result_pool)
+{
+ return checksum_create(svn_checksum_fnv1a_32, digest, result_pool);
+}
+
+svn_checksum_t *
+svn_checksum__from_digest_fnv1a_32x4(const unsigned char *digest,
+ apr_pool_t *result_pool)
+{
+ return checksum_create(svn_checksum_fnv1a_32x4, digest, result_pool);
}
svn_error_t *
@@ -145,9 +263,13 @@ svn_checksum_match(const svn_checksum_t *checksum1,
switch (checksum1->kind)
{
case svn_checksum_md5:
- return svn_md5__digests_match(checksum1->digest, checksum2->digest);
case svn_checksum_sha1:
- return svn_sha1__digests_match(checksum1->digest, checksum2->digest);
+ case svn_checksum_fnv1a_32:
+ case svn_checksum_fnv1a_32x4:
+ return svn__digests_match(checksum1->digest,
+ checksum2->digest,
+ digest_sizes[checksum1->kind]);
+
default:
/* We really shouldn't get here, but if we do... */
return FALSE;
@@ -161,9 +283,13 @@ svn_checksum_to_cstring_display(const svn_checksum_t *checksum,
switch (checksum->kind)
{
case svn_checksum_md5:
- return svn_md5__digest_to_cstring_display(checksum->digest, pool);
case svn_checksum_sha1:
- return svn_sha1__digest_to_cstring_display(checksum->digest, pool);
+ case svn_checksum_fnv1a_32:
+ case svn_checksum_fnv1a_32x4:
+ return svn__digest_to_cstring_display(checksum->digest,
+ digest_sizes[checksum->kind],
+ pool);
+
default:
/* We really shouldn't get here, but if we do... */
return NULL;
@@ -180,9 +306,13 @@ svn_checksum_to_cstring(const svn_checksum_t *checksum,
switch (checksum->kind)
{
case svn_checksum_md5:
- return svn_md5__digest_to_cstring(checksum->digest, pool);
case svn_checksum_sha1:
- return svn_sha1__digest_to_cstring(checksum->digest, pool);
+ case svn_checksum_fnv1a_32:
+ case svn_checksum_fnv1a_32x4:
+ return svn__digest_to_cstring(checksum->digest,
+ digest_sizes[checksum->kind],
+ pool);
+
default:
/* We really shouldn't get here, but if we do... */
return NULL;
@@ -195,15 +325,12 @@ svn_checksum_serialize(const svn_checksum_t *checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- const char *ckind_str;
-
- SVN_ERR_ASSERT_NO_RETURN(checksum->kind == svn_checksum_md5
- || checksum->kind == svn_checksum_sha1);
- ckind_str = (checksum->kind == svn_checksum_md5 ? "$md5 $" : "$sha1$");
+ SVN_ERR_ASSERT_NO_RETURN(checksum->kind >= svn_checksum_md5
+ || checksum->kind <= svn_checksum_fnv1a_32x4);
return apr_pstrcat(result_pool,
- ckind_str,
+ ckind_str[checksum->kind],
svn_checksum_to_cstring(checksum, scratch_pool),
- (char *)NULL);
+ SVN_VA_NULL);
}
@@ -213,18 +340,29 @@ svn_checksum_deserialize(const svn_checksum_t **checksum,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_checksum_kind_t ckind;
+ svn_checksum_kind_t kind;
svn_checksum_t *parsed_checksum;
- /* "$md5 $..." or "$sha1$..." */
- SVN_ERR_ASSERT(strlen(data) > 6);
-
- ckind = (data[1] == 'm' ? svn_checksum_md5 : svn_checksum_sha1);
- SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, ckind,
- data + 6, result_pool));
- *checksum = parsed_checksum;
-
- return SVN_NO_ERROR;
+ /* All prefixes have the same length. */
+ apr_size_t prefix_len = strlen(ckind_str[0]);
+
+ /* "$md5 $...", "$sha1$..." or ... */
+ if (strlen(data) <= prefix_len)
+ return svn_error_createf(SVN_ERR_BAD_CHECKSUM_PARSE, NULL,
+ _("Invalid prefix in checksum '%s'"),
+ data);
+
+ for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
+ if (strncmp(ckind_str[kind], data, prefix_len) == 0)
+ {
+ SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, kind,
+ data + prefix_len, result_pool));
+ *checksum = parsed_checksum;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_createf(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
+ "Unknown checksum kind in '%s'", data);
}
@@ -234,7 +372,7 @@ svn_checksum_parse_hex(svn_checksum_t **checksum,
const char *hex,
apr_pool_t *pool)
{
- int i, len;
+ apr_size_t i, len;
char is_nonzero = '\0';
char *digest;
static const char xdigitval[256] =
@@ -302,11 +440,11 @@ svn_checksum_dup(const svn_checksum_t *checksum,
switch (checksum->kind)
{
case svn_checksum_md5:
- return svn_checksum__from_digest_md5(checksum->digest, pool);
- break;
case svn_checksum_sha1:
- return svn_checksum__from_digest_sha1(checksum->digest, pool);
- break;
+ case svn_checksum_fnv1a_32:
+ case svn_checksum_fnv1a_32x4:
+ return checksum_create(checksum->kind, checksum->digest, pool);
+
default:
SVN_ERR_MALFUNCTION_NO_RETURN();
break;
@@ -337,6 +475,16 @@ svn_checksum(svn_checksum_t **checksum,
apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx);
break;
+ case svn_checksum_fnv1a_32:
+ *(apr_uint32_t *)(*checksum)->digest
+ = htonl(svn__fnv1a_32(data, len));
+ break;
+
+ case svn_checksum_fnv1a_32x4:
+ *(apr_uint32_t *)(*checksum)->digest
+ = htonl(svn__fnv1a_32x4(data, len));
+ break;
+
default:
/* We really shouldn't get here, but if we do... */
return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
@@ -353,12 +501,10 @@ svn_checksum_empty_checksum(svn_checksum_kind_t kind,
switch (kind)
{
case svn_checksum_md5:
- return svn_checksum__from_digest_md5(svn_md5__empty_string_digest(),
- pool);
-
case svn_checksum_sha1:
- return svn_checksum__from_digest_sha1(svn_sha1__empty_string_digest(),
- pool);
+ case svn_checksum_fnv1a_32:
+ case svn_checksum_fnv1a_32x4:
+ return checksum_create(kind, empty_string_digests[kind], pool);
default:
/* We really shouldn't get here, but if we do... */
@@ -391,6 +537,14 @@ svn_checksum_ctx_create(svn_checksum_kind_t kind,
apr_sha1_init(ctx->apr_ctx);
break;
+ case svn_checksum_fnv1a_32:
+ ctx->apr_ctx = svn_fnv1a_32__context_create(pool);
+ break;
+
+ case svn_checksum_fnv1a_32x4:
+ ctx->apr_ctx = svn_fnv1a_32x4__context_create(pool);
+ break;
+
default:
SVN_ERR_MALFUNCTION_NO_RETURN();
}
@@ -413,6 +567,14 @@ svn_checksum_update(svn_checksum_ctx_t *ctx,
apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len);
break;
+ case svn_checksum_fnv1a_32:
+ svn_fnv1a_32__update(ctx->apr_ctx, data, len);
+ break;
+
+ case svn_checksum_fnv1a_32x4:
+ svn_fnv1a_32x4__update(ctx->apr_ctx, data, len);
+ break;
+
default:
/* We really shouldn't get here, but if we do... */
return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
@@ -438,6 +600,16 @@ svn_checksum_final(svn_checksum_t **checksum,
apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
break;
+ case svn_checksum_fnv1a_32:
+ *(apr_uint32_t *)(*checksum)->digest
+ = htonl(svn_fnv1a_32__finalize(ctx->apr_ctx));
+ break;
+
+ case svn_checksum_fnv1a_32x4:
+ *(apr_uint32_t *)(*checksum)->digest
+ = htonl(svn_fnv1a_32x4__finalize(ctx->apr_ctx));
+ break;
+
default:
/* We really shouldn't get here, but if we do... */
return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
@@ -486,15 +658,152 @@ svn_checksum_is_empty_checksum(svn_checksum_t *checksum)
switch (checksum->kind)
{
case svn_checksum_md5:
- return svn_md5__digests_match(checksum->digest,
- svn_md5__empty_string_digest());
-
case svn_checksum_sha1:
- return svn_sha1__digests_match(checksum->digest,
- svn_sha1__empty_string_digest());
+ case svn_checksum_fnv1a_32:
+ case svn_checksum_fnv1a_32x4:
+ return svn__digests_match(checksum->digest,
+ svn__empty_string_digest(checksum->kind),
+ digest_sizes[checksum->kind]);
default:
/* We really shouldn't get here, but if we do... */
SVN_ERR_MALFUNCTION_NO_RETURN();
}
}
+
+/* Checksum calculating stream wrappers.
+ */
+
+/* Baton used by write_handler and close_handler to calculate the checksum
+ * and return the result to the stream creator. It accommodates the data
+ * needed by svn_checksum__wrap_write_stream_fnv1a_32x4 as well as
+ * svn_checksum__wrap_write_stream.
+ */
+typedef struct stream_baton_t
+{
+ /* Stream we are wrapping. Forward write() and close() operations to it. */
+ svn_stream_t *inner_stream;
+
+ /* Build the checksum data in here. */
+ svn_checksum_ctx_t *context;
+
+ /* Write the final checksum here. May be NULL. */
+ svn_checksum_t **checksum;
+
+ /* Copy the digest of the final checksum. May be NULL. */
+ unsigned char *digest;
+
+ /* Allocate the resulting checksum here. */
+ apr_pool_t *pool;
+} stream_baton_t;
+
+/* Implement svn_write_fn_t.
+ * Update checksum and pass data on to inner stream.
+ */
+static svn_error_t *
+write_handler(void *baton,
+ const char *data,
+ apr_size_t *len)
+{
+ stream_baton_t *b = baton;
+
+ SVN_ERR(svn_checksum_update(b->context, data, *len));
+ SVN_ERR(svn_stream_write(b->inner_stream, data, len));
+
+ return SVN_NO_ERROR;
+}
+
+/* Implement svn_close_fn_t.
+ * Finalize checksum calculation and write results. Close inner stream.
+ */
+static svn_error_t *
+close_handler(void *baton)
+{
+ stream_baton_t *b = baton;
+ svn_checksum_t *local_checksum;
+
+ /* Ensure we can always write to *B->CHECKSUM. */
+ if (!b->checksum)
+ b->checksum = &local_checksum;
+
+ /* Get the final checksum. */
+ SVN_ERR(svn_checksum_final(b->checksum, b->context, b->pool));
+
+ /* Extract digest, if wanted. */
+ if (b->digest)
+ {
+ apr_size_t digest_size = DIGESTSIZE((*b->checksum)->kind);
+ memcpy(b->digest, (*b->checksum)->digest, digest_size);
+ }
+
+ /* Done here. Now, close the underlying stream as well. */
+ return svn_error_trace(svn_stream_close(b->inner_stream));
+}
+
+/* Common constructor function for svn_checksum__wrap_write_stream and
+ * svn_checksum__wrap_write_stream_fnv1a_32x4, taking the superset of their
+ * respecting parameters.
+ *
+ * In the current usage, either CHECKSUM or DIGEST will be NULL but this
+ * function does not enforce any such restriction. Also, the caller must
+ * make sure that DIGEST refers to a buffer of sufficient length.
+ */
+static svn_stream_t *
+wrap_write_stream(svn_checksum_t **checksum,
+ unsigned char *digest,
+ svn_stream_t *inner_stream,
+ svn_checksum_kind_t kind,
+ apr_pool_t *pool)
+{
+ svn_stream_t *outer_stream;
+
+ stream_baton_t *baton = apr_pcalloc(pool, sizeof(*baton));
+ baton->inner_stream = inner_stream;
+ baton->context = svn_checksum_ctx_create(kind, pool);
+ baton->checksum = checksum;
+ baton->digest = digest;
+ baton->pool = pool;
+
+ outer_stream = svn_stream_create(baton, pool);
+ svn_stream_set_write(outer_stream, write_handler);
+ svn_stream_set_close(outer_stream, close_handler);
+
+ return outer_stream;
+}
+
+svn_stream_t *
+svn_checksum__wrap_write_stream(svn_checksum_t **checksum,
+ svn_stream_t *inner_stream,
+ svn_checksum_kind_t kind,
+ apr_pool_t *pool)
+{
+ return wrap_write_stream(checksum, NULL, inner_stream, kind, pool);
+}
+
+/* Implement svn_close_fn_t.
+ * For FNV-1a-like checksums, we want the checksum as 32 bit integer instead
+ * of a big endian 4 byte sequence. This simply wraps close_handler adding
+ * the digest conversion.
+ */
+static svn_error_t *
+close_handler_fnv1a_32x4(void *baton)
+{
+ stream_baton_t *b = baton;
+ SVN_ERR(close_handler(baton));
+
+ *(apr_uint32_t *)b->digest = ntohl(*(apr_uint32_t *)b->digest);
+ return SVN_NO_ERROR;
+}
+
+svn_stream_t *
+svn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t *digest,
+ svn_stream_t *inner_stream,
+ apr_pool_t *pool)
+{
+ svn_stream_t *result
+ = wrap_write_stream(NULL, (unsigned char *)digest, inner_stream,
+ svn_checksum_fnv1a_32x4, pool);
+ svn_stream_set_close(result, close_handler_fnv1a_32x4);
+
+ return result;
+}