sha1 name generator

This commit is contained in:
Marius Bancila 2018-01-17 12:17:31 +02:00
parent 9b1ef4f116
commit ea43f4b9ba
2 changed files with 316 additions and 30 deletions

View File

@ -9,6 +9,7 @@
#include <random>
#include <memory>
#include <functional>
#include <type_traits>
#include <assert.h>
#ifdef _WIN32
@ -49,6 +50,186 @@ namespace uuids
{
return (hex2char(a) << 4) | hex2char(b);
}
class sha1
{
public:
typedef uint32_t digest32_t[5];
typedef uint8_t digest8_t[20];
static constexpr unsigned int block_bytes = 64;
inline static uint32_t left_rotate(uint32_t value, size_t const count)
{
return (value << count) ^ (value >> (32 - count));
}
sha1() { reset(); }
void reset()
{
m_digest[0] = 0x67452301;
m_digest[1] = 0xEFCDAB89;
m_digest[2] = 0x98BADCFE;
m_digest[3] = 0x10325476;
m_digest[4] = 0xC3D2E1F0;
m_blockByteIndex = 0;
m_byteCount = 0;
}
void process_byte(uint8_t octet)
{
this->m_block[this->m_blockByteIndex++] = octet;
++this->m_byteCount;
if (m_blockByteIndex == block_bytes)
{
this->m_blockByteIndex = 0;
process_block();
}
}
void process_block(void const * const start, void const * const end)
{
const uint8_t* begin = static_cast<const uint8_t*>(start);
const uint8_t* finish = static_cast<const uint8_t*>(end);
while (begin != finish)
{
process_byte(*begin);
begin++;
}
}
void process_bytes(void const * const data, size_t const len)
{
const uint8_t* block = static_cast<const uint8_t*>(data);
process_block(block, block + len);
}
uint32_t const * get_digest(digest32_t digest)
{
size_t const bitCount = this->m_byteCount * 8;
process_byte(0x80);
if (this->m_blockByteIndex > 56) {
while (m_blockByteIndex != 0) {
process_byte(0);
}
while (m_blockByteIndex < 56) {
process_byte(0);
}
}
else {
while (m_blockByteIndex < 56) {
process_byte(0);
}
}
process_byte(0);
process_byte(0);
process_byte(0);
process_byte(0);
process_byte(static_cast<unsigned char>((bitCount >> 24) & 0xFF));
process_byte(static_cast<unsigned char>((bitCount >> 16) & 0xFF));
process_byte(static_cast<unsigned char>((bitCount >> 8) & 0xFF));
process_byte(static_cast<unsigned char>((bitCount) & 0xFF));
memcpy(digest, m_digest, 5 * sizeof(uint32_t));
return digest;
}
uint8_t const * get_digest_bytes(digest8_t digest)
{
digest32_t d32;
get_digest(d32);
size_t di = 0;
digest[di++] = ((d32[0] >> 24) & 0xFF);
digest[di++] = ((d32[0] >> 16) & 0xFF);
digest[di++] = ((d32[0] >> 8) & 0xFF);
digest[di++] = ((d32[0]) & 0xFF);
digest[di++] = ((d32[1] >> 24) & 0xFF);
digest[di++] = ((d32[1] >> 16) & 0xFF);
digest[di++] = ((d32[1] >> 8) & 0xFF);
digest[di++] = ((d32[1]) & 0xFF);
digest[di++] = ((d32[2] >> 24) & 0xFF);
digest[di++] = ((d32[2] >> 16) & 0xFF);
digest[di++] = ((d32[2] >> 8) & 0xFF);
digest[di++] = ((d32[2]) & 0xFF);
digest[di++] = ((d32[3] >> 24) & 0xFF);
digest[di++] = ((d32[3] >> 16) & 0xFF);
digest[di++] = ((d32[3] >> 8) & 0xFF);
digest[di++] = ((d32[3]) & 0xFF);
digest[di++] = ((d32[4] >> 24) & 0xFF);
digest[di++] = ((d32[4] >> 16) & 0xFF);
digest[di++] = ((d32[4] >> 8) & 0xFF);
digest[di++] = ((d32[4]) & 0xFF);
return digest;
}
private:
void process_block()
{
uint32_t w[80];
for (size_t i = 0; i < 16; i++) {
w[i] = (m_block[i * 4 + 0] << 24);
w[i] |= (m_block[i * 4 + 1] << 16);
w[i] |= (m_block[i * 4 + 2] << 8);
w[i] |= (m_block[i * 4 + 3]);
}
for (size_t i = 16; i < 80; i++) {
w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1);
}
uint32_t a = m_digest[0];
uint32_t b = m_digest[1];
uint32_t c = m_digest[2];
uint32_t d = m_digest[3];
uint32_t e = m_digest[4];
for (std::size_t i = 0; i < 80; ++i)
{
uint32_t f = 0;
uint32_t k = 0;
if (i < 20) {
f = (b & c) | (~b & d);
k = 0x5A827999;
}
else if (i < 40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
}
else if (i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
}
else {
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
uint32_t temp = left_rotate(a, 5) + f + e + k + w[i];
e = d;
d = c;
c = left_rotate(b, 30);
b = a;
a = temp;
}
m_digest[0] += a;
m_digest[1] += b;
m_digest[2] += c;
m_digest[3] += d;
m_digest[4] += e;
}
private:
digest32_t m_digest;
uint8_t m_block[64];
size_t m_blockByteIndex;
size_t m_byteCount;
};
}
// UUID format https://tools.ietf.org/html/rfc4122
@ -724,6 +905,78 @@ namespace uuids
};
using uuid_random_generator = basic_uuid_random_generator<std::mt19937>;
class uuid_name_generator
{
public:
typedef uuid result_type;
explicit uuid_name_generator(uuid const& namespace_uuid) noexcept
: nsuuid(namespace_uuid)
{}
uuid operator()(std::string_view name)
{
reset();
process_characters(name.data(), name.size());
return make_uuid();
}
uuid operator()(std::wstring_view name)
{
reset();
process_characters(name.data(), name.size());
return make_uuid();
}
private:
void reset()
{
hasher.reset();
uint8_t bytes[uuid::state_size];
std::copy(std::begin(nsuuid), std::end(nsuuid), bytes);
hasher.process_bytes(bytes, uuid::state_size);
}
template <typename char_type,
typename = std::enable_if_t<std::is_integral<char_type>::value>>
void process_characters(char_type const * const characters, size_t const count)
{
for (size_t i = 0; i < count; i++)
{
uint32_t c = characters[i];
hasher.process_byte(static_cast<unsigned char>((c >> 0) & 0xFF));
hasher.process_byte(static_cast<unsigned char>((c >> 8) & 0xFF));
hasher.process_byte(static_cast<unsigned char>((c >> 16) & 0xFF));
hasher.process_byte(static_cast<unsigned char>((c >> 24) & 0xFF));
}
}
void process_characters(const char * const characters, size_t const count)
{
hasher.process_bytes(characters, count);
}
uuid make_uuid()
{
detail::sha1::digest8_t digest;
hasher.get_digest_bytes(digest);
// variant must be 0b10xxxxxx
digest[8] &= 0xBF;
digest[8] |= 0x80;
// version must be 0b0101xxxx
digest[6] &= 0x5F;
digest[6] |= 0x50;
return uuid{ digest, digest + uuid::state_size };
}
private:
uuid nsuuid;
detail::sha1 hasher;
};
}
namespace std

View File

@ -231,10 +231,10 @@ int main()
assert(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(!id2.nil());
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
@ -256,10 +256,10 @@ int main()
assert(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(!id2.nil());
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
@ -282,9 +282,9 @@ int main()
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
@ -306,10 +306,10 @@ int main()
assert(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(!id2.nil());
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
@ -326,9 +326,9 @@ int main()
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
@ -347,10 +347,10 @@ int main()
assert(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(!id2.nil());
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
@ -369,10 +369,10 @@ int main()
assert(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(!id2.nil());
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
@ -391,14 +391,47 @@ int main()
assert(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::random_number_based);
assert(id1.variant() == uuids::uuid_variant::rfc);
assert(!id2.nil());
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::random_number_based);
assert(id2.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
}
{
std::cout << "Test name generator" << std::endl;
uuids::uuid_name_generator dgen(uuids::uuid_default_generator{}());
auto id1 = dgen("john");
assert(!id1.nil());
assert(id1.size() == 16);
assert(id1.version() == uuids::uuid_version::name_based_sha1);
assert(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen("jane");
assert(!id2.nil());
assert(id2.size() == 16);
assert(id2.version() == uuids::uuid_version::name_based_sha1);
assert(id2.variant() == uuids::uuid_variant::rfc);
auto id3 = dgen("jane");
assert(!id3.nil());
assert(id3.size() == 16);
assert(id3.version() == uuids::uuid_version::name_based_sha1);
assert(id3.variant() == uuids::uuid_variant::rfc);
auto id4 = dgen(L"jane");
assert(!id4.nil());
assert(id4.size() == 16);
assert(id4.version() == uuids::uuid_version::name_based_sha1);
assert(id4.variant() == uuids::uuid_variant::rfc);
assert(id1 != id2);
assert(id2 == id3);
assert(id3 != id4);
}
std::cout << "ALL PASSED" << std::endl;
return 0;