patternMinor
Truncate 160-bit output from SHA-1 to 64-bit uint64_t
Viewed 0 times
bit160shaoutputuint64_ttruncatefrom
Problem
I'd like to truncate 160-bit output from SHA-1 to receive a (weaker) 64-bit digest.
It has been a while since I did the type of low-level C pointer arithmetic in the but-last line. Could you please review it for me? It should return the last 64 bits of the the byte array in a
How could this code be improved upon?
UPDATE Here is a second (alternative) version with an attempt at avoiding any alignment issues. (Its much more straigtforward to truncate to the first 64 bits in this case.) Actual digests will differ from the 1st version.
It has been a while since I did the type of low-level C pointer arithmetic in the but-last line. Could you please review it for me? It should return the last 64 bits of the the byte array in a
uint64_t. (I guess conceptually I could just as well take the first 64 bits, which would make the code simpler but here we are.)#import
- (uint64_t)digestFromString: (NSString *)s {
NSData *input = [s dataUsingEncoding: NSUTF8StringEncoding];
unsigned char output[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(input.bytes, (CC_LONG)input.length, output);
uint64_t digest;
NSAssert(CC_SHA1_DIGEST_LENGTH >= sizeof(uint64_t), nil);
digest = *(uint64_t *)(output + CC_SHA1_DIGEST_LENGTH - sizeof(uint64_t));
return digest;
}How could this code be improved upon?
UPDATE Here is a second (alternative) version with an attempt at avoiding any alignment issues. (Its much more straigtforward to truncate to the first 64 bits in this case.) Actual digests will differ from the 1st version.
- (uint64_t)digestFromString: (NSString *)s {
NSData *input = [s dataUsingEncoding: NSUTF8StringEncoding];
union { // uint64_t-aligned
uint64_t uint64s[CC_SHA1_DIGEST_LENGTH / sizeof(uint64_t) + sizeof(uint64_t)];
unsigned char bytes[CC_SHA1_DIGEST_LENGTH];
} output;
CC_SHA1(input.bytes, (CC_LONG)input.length, (unsigned char *)output.bytes);
return output.uint64s[0];
}Solution
Both your methods should work. As far as I know, there is no alignment problem
on the current OS X and iOS architectures in your first method (see below).
The code from the second method can be simplified a tiny bit:
The first element in the union need not be an array, and casting
to
to the first element when passed as a function argument).
But the easiest method to extract a 64-bit integer from the digest would be to use the inline function
from OSByteOrder.h that comes with the OS X and iOS SDKs:
This function is implemented as
which indicates that there is no alignment problem when casting an
arbitrary pointer to
architectures, with using the above function you are on the safe side.
Alternatively, you can use
Instead of
you could use a "compile-time/static assertion":
compiler, so you can use it in your Xcode projects.
The advantage is that it causes a compiler error if the condition is
not satisfied, instead of failing later at runtime.
on the current OS X and iOS architectures in your first method (see below).
The code from the second method can be simplified a tiny bit:
- (uint64_t)digestFromString: (NSString *)s {
NSData *input = [s dataUsingEncoding: NSUTF8StringEncoding];
union { // uint64_t-aligned
uint64_t digest;
unsigned char bytes[CC_SHA1_DIGEST_LENGTH];
} output;
CC_SHA1(input.bytes, (CC_LONG)input.length, output.bytes);
return output.digest;
}The first element in the union need not be an array, and casting
output.bytesto
unsigned char * is not necessary (the array automatically becomes a pointerto the first element when passed as a function argument).
But the easiest method to extract a 64-bit integer from the digest would be to use the inline function
OS_INLINE uint64_t _OSReadInt64(
const volatile void * base,
uintptr_t byteOffset
)from OSByteOrder.h that comes with the OS X and iOS SDKs:
uint64_t digest = _OSReadInt64(output, CC_SHA1_DIGEST_LENGTH - sizeof(uint64_t)); // last 8 bytes
uint64_t digest = _OSReadInt64(output, 0); // first 8 bytesThis function is implemented as
return *(volatile uint64_t *)((uintptr_t)base + byteOffset);which indicates that there is no alignment problem when casting an
arbitrary pointer to
uint64_t *. But that may be different on future architectures, with using the above function you are on the safe side.
Alternatively, you can use
memcpy:uint64_t digest;
memcpy(&digest, output, sizeof digest);Instead of
NSAssert(CC_SHA1_DIGEST_LENGTH >= sizeof(uint64_t), nil);you could use a "compile-time/static assertion":
_Static_assert(CC_SHA1_DIGEST_LENGTH >= sizeof(uint64_t), "");_Static_assert is a GCC extension which is also understood by the Clangcompiler, so you can use it in your Xcode projects.
The advantage is that it causes a compiler error if the condition is
not satisfied, instead of failing later at runtime.
Code Snippets
- (uint64_t)digestFromString: (NSString *)s {
NSData *input = [s dataUsingEncoding: NSUTF8StringEncoding];
union { // uint64_t-aligned
uint64_t digest;
unsigned char bytes[CC_SHA1_DIGEST_LENGTH];
} output;
CC_SHA1(input.bytes, (CC_LONG)input.length, output.bytes);
return output.digest;
}OS_INLINE uint64_t _OSReadInt64(
const volatile void * base,
uintptr_t byteOffset
)uint64_t digest = _OSReadInt64(output, CC_SHA1_DIGEST_LENGTH - sizeof(uint64_t)); // last 8 bytes
uint64_t digest = _OSReadInt64(output, 0); // first 8 bytesreturn *(volatile uint64_t *)((uintptr_t)base + byteOffset);uint64_t digest;
memcpy(&digest, output, sizeof digest);Context
StackExchange Code Review Q#79489, answer score: 6
Revisions (0)
No revisions yet.