From 0cb3c286883b694fc52a18a3b559ff98931641f3 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 6 Oct 2015 15:04:42 +0200 Subject: siphash24: unify API Make the API of the new helpers more similar to the old wrapper. In particular we now return the hash as a byte string to avoid any endianness problems. --- src/basic/hashmap.c | 7 +++++-- src/basic/siphash24.c | 15 +++++++-------- src/basic/siphash24.h | 4 ++-- src/journal/journald-rate-limit.c | 12 ++++++------ src/libsystemd-network/sd-dhcp-server.c | 6 ++++-- src/libsystemd-network/test-dhcp-server.c | 7 +++++-- src/test/test-siphash24.c | 24 ++++++++---------------- 7 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 3e17ed30df..20e7e51d9e 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -372,12 +372,15 @@ static uint8_t *hash_key(HashmapBase *h) { static unsigned base_bucket_hash(HashmapBase *h, const void *p) { struct siphash state; + uint64_t hash; - siphash_init(&state, hash_key(h)); + siphash24_init(&state, hash_key(h)); h->hash_ops->hash(p, &state); - return (unsigned) (siphash24_finalize(&state) % n_buckets(h)); + siphash24_finalize((uint8_t*)&hash, &state); + + return (unsigned) (hash % n_buckets(h)); } #define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c index 308e4230c5..9c56b5e9ea 100644 --- a/src/basic/siphash24.c +++ b/src/basic/siphash24.c @@ -52,7 +52,7 @@ typedef uint8_t u8; (state)->v2 += (state)->v1; (state)->v1=ROTL((state)->v1,17); (state)->v1 ^= (state)->v2; (state)->v2=ROTL((state)->v2,32); \ } while(0) -void siphash_init(struct siphash *state, const uint8_t k[16]) { +void siphash24_init(struct siphash *state, const uint8_t k[16]) { u64 k0, k1; k0 = U8TO64_LE( k ); @@ -140,7 +140,7 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { } } -uint64_t siphash24_finalize(struct siphash *state) { +void siphash24_finalize(uint8_t out[8], struct siphash *state) { u64 b; b = state->padding | (( ( u64 )state->inlen ) << 56); @@ -168,20 +168,19 @@ uint64_t siphash24_finalize(struct siphash *state) { SIPROUND(state); SIPROUND(state); - return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; + b = state->v0 ^ state->v1 ^ state->v2 ^ state->v3; + + U64TO8_LE( out, b ); } /* SipHash-2-4 */ void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) { struct siphash state; - u64 b; - siphash_init(&state, k); + siphash24_init(&state, k); siphash24_compress(_in, inlen, &state); - b = siphash24_finalize(&state); - - U64TO8_LE( out, b ); + siphash24_finalize(out, &state); } diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h index c107bdd213..6c5cd98ee8 100644 --- a/src/basic/siphash24.h +++ b/src/basic/siphash24.h @@ -12,8 +12,8 @@ struct siphash { size_t inlen; }; -void siphash_init(struct siphash *state, const uint8_t k[16]); +void siphash24_init(struct siphash *state, const uint8_t k[16]); void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -uint64_t siphash24_finalize(struct siphash *state); +void siphash24_finalize(uint8_t out[8], struct siphash *state); void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index 7e06117b31..8afd493b50 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -57,7 +57,7 @@ struct JournalRateLimitGroup { char *id; JournalRateLimitPool pools[POOLS_MAX]; - unsigned long hash; + uint64_t hash; LIST_FIELDS(JournalRateLimitGroup, bucket); LIST_FIELDS(JournalRateLimitGroup, lru); @@ -158,9 +158,9 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, if (!g->id) goto fail; - siphash_init(&state, r->hash_key); + siphash24_init(&state, r->hash_key); string_hash_func(g->id, &state); - g->hash = siphash24_finalize(&state); + siphash24_finalize((uint8_t*)&g->hash, &state); journal_rate_limit_vacuum(r, ts); @@ -207,7 +207,7 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) { } int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) { - unsigned long h; + uint64_t h; JournalRateLimitGroup *g; JournalRateLimitPool *p; struct siphash state; @@ -226,9 +226,9 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u ts = now(CLOCK_MONOTONIC); - siphash_init(&state, r->hash_key); + siphash24_init(&state, r->hash_key); string_hash_func(id, &state); - h = siphash24_finalize(&state); + siphash24_finalize((uint8_t*)&h, &state); g = r->buckets[h % BUCKETS_MAX]; LIST_FOREACH(bucket, g, g) diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index d941e6c0de..d27bb561ca 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -741,15 +741,17 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, address = existing_lease->address; else { struct siphash state; + uint64_t hash; uint32_t next_offer; /* even with no persistence of leases, we try to offer the same client the same IP address. we do this by using the hash of the client id as the offset into the pool of leases when finding the next free one */ - siphash_init(&state, HASH_KEY.bytes); + siphash24_init(&state, HASH_KEY.bytes); client_id_hash_func(&req->client_id, &state); - next_offer = siphash24_finalize(&state) % server->pool_size; + siphash24_finalize((uint8_t*)&hash, &state); + next_offer = hash % server->pool_size; for (i = 0; i < server->pool_size; i++) { if (!server->bound_leases[next_offer]) { diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index 01205efc18..c3bcb9cb4b 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -200,10 +200,13 @@ static void test_message_handler(void) { static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) { struct siphash state; + uint64_t hash; - siphash_init(&state, key); + siphash24_init(&state, key); client_id_hash_func(id, &state); - return siphash24_finalize(&state); + siphash24_finalize((uint8_t*)&hash, &state); + + return hash; } static void test_client_id_hash(void) { diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c index 65eb2b6f35..2402da6a6f 100644 --- a/src/test/test-siphash24.c +++ b/src/test/test-siphash24.c @@ -32,23 +32,13 @@ int main(int argc, char *argv[]) { const uint8_t key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; uint64_t out = 0; - unsigned i, j, k; - usec_t ts; + unsigned i, j; siphash24((uint8_t *)&out, in, sizeof(in), key); - assert_se(out == 0xa129ca6149be45e5); - - assert_se(out == 0xa129ca6149be45e5ULL); - - ts = now(CLOCK_MONOTONIC); - for (k = 0; k < ITERATIONS; k++) - siphash24((uint8_t *)&out, in, sizeof(in), key); - ts = now(CLOCK_MONOTONIC) - ts; - - log_info("%llu iterations per second", (ITERATIONS * USEC_PER_SEC) / ts); + assert_se(out == htole64(0xa129ca6149be45e5)); /* verify the internal state as given in the above paper */ - siphash_init(&state, key); + siphash24_init(&state, key); assert_se(state.v0 == 0x7469686173716475); assert_se(state.v1 == 0x6b617f6d656e6665); assert_se(state.v2 == 0x6b7f62616d677361); @@ -58,7 +48,8 @@ int main(int argc, char *argv[]) { assert_se(state.v1 == 0x0d52f6f62a4f59a4); assert_se(state.v2 == 0x634cb3577b01fd3d); assert_se(state.v3 == 0xa5224d6f55c7d9c8); - assert_se(siphash24_finalize(&state) == 0xa129ca6149be45e5); + siphash24_finalize((uint8_t*)&out, &state); + assert_se(out == htole64(0xa129ca6149be45e5)); assert_se(state.v0 == 0xf6bcd53893fecff1); assert_se(state.v1 == 0x54b9964c7ea0d937); assert_se(state.v2 == 0x1b38329c099bb55a); @@ -68,11 +59,12 @@ int main(int argc, char *argv[]) { same result */ for (i = 0; i < sizeof(in); i++) { for (j = i; j < sizeof(in); j++) { - siphash_init(&state, key); + siphash24_init(&state, key); siphash24_compress(in, i, &state); siphash24_compress(&in[i], j - i, &state); siphash24_compress(&in[j], sizeof(in) - j, &state); - assert_se(siphash24_finalize(&state) == 0xa129ca6149be45e5); + siphash24_finalize((uint8_t*)&out, &state); + assert_se(out == htole64(0xa129ca6149be45e5)); } } } -- cgit v1.2.3-54-g00ecf From 17010c230edf9933176948934276cf18007ccadb Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 6 Oct 2015 16:31:58 +0200 Subject: siphash24: coding-style fixes Drop custom types. Drop unnecessary macros. Fix whitespace. Add asserts. --- src/basic/siphash24.c | 248 +++++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 126 deletions(-) diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c index 9c56b5e9ea..3b61961389 100644 --- a/src/basic/siphash24.c +++ b/src/basic/siphash24.c @@ -13,174 +13,170 @@ this software. If not, see . (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd) + (Refactored by Tom Gundersen to split up in several functions and follow systemd + coding style) */ -#include -#include -#include + +#include "sparse-endian.h" #include "siphash24.h" +#include "util.h" + +static inline uint64_t rotate_left(uint64_t x, uint8_t b) { + assert(b < 64); + + return (x << b) | (x >> (64 - b)); +} -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint8_t u8; - -#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) ) - -#define U32TO8_LE(p, v) \ - (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \ - (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24); - -#define U64TO8_LE(p, v) \ - U32TO8_LE((p), (u32)((v) )); \ - U32TO8_LE((p) + 4, (u32)((v) >> 32)); - -#define U8TO64_LE(p) \ - (((u64)((p)[0]) ) | \ - ((u64)((p)[1]) << 8) | \ - ((u64)((p)[2]) << 16) | \ - ((u64)((p)[3]) << 24) | \ - ((u64)((p)[4]) << 32) | \ - ((u64)((p)[5]) << 40) | \ - ((u64)((p)[6]) << 48) | \ - ((u64)((p)[7]) << 56)) - -#define SIPROUND(state) \ - do { \ - (state)->v0 += (state)->v1; (state)->v1=ROTL((state)->v1,13); (state)->v1 ^= (state)->v0; (state)->v0=ROTL((state)->v0,32); \ - (state)->v2 += (state)->v3; (state)->v3=ROTL((state)->v3,16); (state)->v3 ^= (state)->v2; \ - (state)->v0 += (state)->v3; (state)->v3=ROTL((state)->v3,21); (state)->v3 ^= (state)->v0; \ - (state)->v2 += (state)->v1; (state)->v1=ROTL((state)->v1,17); (state)->v1 ^= (state)->v2; (state)->v2=ROTL((state)->v2,32); \ - } while(0) +static inline void sipround(struct siphash *state) { + assert(state); + + state->v0 += state->v1; + state->v1 = rotate_left(state->v1, 13); + state->v1 ^= state->v0; + state->v0 = rotate_left(state->v0, 32); + state->v2 += state->v3; + state->v3 = rotate_left(state->v3, 16); + state->v3 ^= state->v2; + state->v0 += state->v3; + state->v3 = rotate_left(state->v3, 21); + state->v3 ^= state->v0; + state->v2 += state->v1; + state->v1 = rotate_left(state->v1, 17); + state->v1 ^= state->v2; + state->v2 = rotate_left(state->v2, 32); +} void siphash24_init(struct siphash *state, const uint8_t k[16]) { - u64 k0, k1; - - k0 = U8TO64_LE( k ); - k1 = U8TO64_LE( k + 8 ); - - /* "somepseudorandomlygeneratedbytes" */ - state->v0 = 0x736f6d6570736575ULL ^ k0; - state->v1 = 0x646f72616e646f6dULL ^ k1; - state->v2 = 0x6c7967656e657261ULL ^ k0; - state->v3 = 0x7465646279746573ULL ^ k1; - state->padding = 0; - state->inlen = 0; + uint64_t k0, k1; + + assert(state); + assert(k); + + k0 = le64toh(*(le64_t*) k); + k1 = le64toh(*(le64_t*) (k + 8)); + + /* "somepseudorandomlygeneratedbytes" */ + state->v0 = 0x736f6d6570736575ULL ^ k0; + state->v1 = 0x646f72616e646f6dULL ^ k1; + state->v2 = 0x6c7967656e657261ULL ^ k0; + state->v3 = 0x7465646279746573ULL ^ k1; + state->padding = 0; + state->inlen = 0; } void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { - u64 m; - const u8 *in = _in; - const u8 *end = in + inlen; - int left = state->inlen & 7; + uint64_t m; + const uint8_t *in = _in; + const uint8_t *end = in + inlen; + unsigned left = state->inlen & 7; + + assert(in); + assert(state); - /* update total length */ - state->inlen += inlen; + /* update total length */ + state->inlen += inlen; - /* if padding exists, fill it out */ - if (left > 0) { - for ( ; in < end && left < 8; in ++, left ++ ) - state->padding |= ( ( u64 )*in ) << (left * 8); + /* if padding exists, fill it out */ + if (left > 0) { + for ( ; in < end && left < 8; in ++, left ++ ) + state->padding |= ( ( uint64_t )*in ) << (left * 8); - if (in == end && left < 8) - /* we did not have enough input to fill out the padding completely */ - return; + if (in == end && left < 8) + /* we did not have enough input to fill out the padding completely */ + return; #ifdef DEBUG - printf( "(%3d) v0 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v0 >> 32 ), ( u32 )state->v0 ); - printf( "(%3d) v1 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v1 >> 32 ), ( u32 )state->v1 ); - printf( "(%3d) v2 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v2 >> 32 ), ( u32 )state->v2 ); - printf( "(%3d) v3 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v3 >> 32 ), ( u32 )state->v3 ); - printf( "(%3d) compress padding %08x %08x\n", ( int )state->inlen, ( u32 )( state->padding >> 32 ), ( u32 )state->padding ); + printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); + printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); + printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); + printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); + printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding); #endif - state->v3 ^= state->padding; - SIPROUND(state); - SIPROUND(state); - state->v0 ^= state->padding; + state->v3 ^= state->padding; + sipround(state); + sipround(state); + state->v0 ^= state->padding; - state->padding = 0; - } + state->padding = 0; + } - end -= ( state->inlen % sizeof (u64) ); + end -= ( state->inlen % sizeof (uint64_t) ); - for ( ; in < end; in += 8 ) - { - m = U8TO64_LE( in ); + for ( ; in < end; in += 8 ) { + m = le64toh(*(le64_t*) in); #ifdef DEBUG - printf( "(%3d) v0 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v0 >> 32 ), ( u32 )state->v0 ); - printf( "(%3d) v1 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v1 >> 32 ), ( u32 )state->v1 ); - printf( "(%3d) v2 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v2 >> 32 ), ( u32 )state->v2 ); - printf( "(%3d) v3 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v3 >> 32 ), ( u32 )state->v3 ); - printf( "(%3d) compress %08x %08x\n", ( int )state->inlen, ( u32 )( m >> 32 ), ( u32 )m ); + printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); + printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); + printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); + printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); + printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m); #endif - state->v3 ^= m; - SIPROUND(state); - SIPROUND(state); - state->v0 ^= m; - } + state->v3 ^= m; + sipround(state); + sipround(state); + state->v0 ^= m; + } - left = state->inlen & 7; + left = state->inlen & 7; - switch( left ) - { - case 7: state->padding |= ( ( u64 )in[ 6] ) << 48; + switch(left) + { + case 7: state->padding |= ((uint64_t) in[6]) << 48; - case 6: state->padding |= ( ( u64 )in[ 5] ) << 40; + case 6: state->padding |= ((uint64_t) in[5]) << 40; - case 5: state->padding |= ( ( u64 )in[ 4] ) << 32; + case 5: state->padding |= ((uint64_t) in[4]) << 32; - case 4: state->padding |= ( ( u64 )in[ 3] ) << 24; + case 4: state->padding |= ((uint64_t) in[3]) << 24; - case 3: state->padding |= ( ( u64 )in[ 2] ) << 16; + case 3: state->padding |= ((uint64_t) in[2]) << 16; - case 2: state->padding |= ( ( u64 )in[ 1] ) << 8; + case 2: state->padding |= ((uint64_t) in[1]) << 8; - case 1: state->padding |= ( ( u64 )in[ 0] ); break; + case 1: state->padding |= ((uint64_t) in[0]); break; - case 0: break; - } + case 0: break; + } } void siphash24_finalize(uint8_t out[8], struct siphash *state) { - u64 b; + uint64_t b; - b = state->padding | (( ( u64 )state->inlen ) << 56); + b = state->padding | (( ( uint64_t )state->inlen ) << 56); #ifdef DEBUG - printf( "(%3d) v0 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v0 >> 32 ), ( u32 )state->v0 ); - printf( "(%3d) v1 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v1 >> 32 ), ( u32 )state->v1 ); - printf( "(%3d) v2 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v2 >> 32 ), ( u32 )state->v2 ); - printf( "(%3d) v3 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v3 >> 32 ), ( u32 )state->v3 ); - printf( "(%3d) padding %08x %08x\n", ( int )state->inlen, ( u32 )( state->padding >> 32 ), ( u32 )state->padding ); + printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t)state->v0); + printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t)state->v1); + printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t)state->v2); + printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t)state->v3); + printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); #endif - state->v3 ^= b; - SIPROUND(state); - SIPROUND(state); - state->v0 ^= b; + state->v3 ^= b; + sipround(state); + sipround(state); + state->v0 ^= b; #ifdef DEBUG - printf( "(%3d) v0 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v0 >> 32 ), ( u32 )state->v0 ); - printf( "(%3d) v1 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v1 >> 32 ), ( u32 )state->v1 ); - printf( "(%3d) v2 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v2 >> 32 ), ( u32 )state->v2 ); - printf( "(%3d) v3 %08x %08x\n", ( int )state->inlen, ( u32 )( state->v3 >> 32 ), ( u32 )state->v3 ); + printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); + printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); + printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); + printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); #endif - state->v2 ^= 0xff; - SIPROUND(state); - SIPROUND(state); - SIPROUND(state); - SIPROUND(state); + state->v2 ^= 0xff; - b = state->v0 ^ state->v1 ^ state->v2 ^ state->v3; + sipround(state); + sipround(state); + sipround(state); + sipround(state); - U64TO8_LE( out, b ); + *(le64_t*)out = htole64(state->v0 ^ state->v1 ^ state->v2 ^ state->v3); } /* SipHash-2-4 */ -void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) -{ - struct siphash state; - - siphash24_init(&state, k); - - siphash24_compress(_in, inlen, &state); +void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) { + struct siphash state; - siphash24_finalize(out, &state); + siphash24_init(&state, k); + siphash24_compress(_in, inlen, &state); + siphash24_finalize(out, &state); } -- cgit v1.2.3-54-g00ecf