From 64748b7b1d41bfbec365825d9080a31f0819caff Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 19 Sep 2015 18:49:54 -0400 Subject: [PATCH] Internals: Replace FNV hashes with SHA1 --- src/V3EmitC.cpp | 2 +- src/V3String.cpp | 212 ++++++++++++++++++++++++++++++++++++++++++++++ src/V3String.h | 77 ++++++++--------- src/Verilator.cpp | 2 + 4 files changed, 251 insertions(+), 42 deletions(-) diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index c4f07bfc8..618ab77d5 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -1560,7 +1560,7 @@ void EmitCImp::emitSavableImp(AstNodeModule* modp) { puts("void "+modClassName(modp)+"::"+funcname+"("+classname+"& os) {\n"); // Place a computed checksum to insure proper structure save/restore formatting // OK if this hash includes some things we won't dump, since just looking for loading the wrong model - VHashFnv hash; + VHashSha1 hash; for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstVar* varp = nodep->castVar()) { hash.insert(varp->name()); diff --git a/src/V3String.cpp b/src/V3String.cpp index 46b73a9e6..fa417195e 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -22,6 +22,7 @@ #include "verilatedos.h" #include "V3String.h" +#include "V3Error.h" //###################################################################### // Wildcard @@ -70,3 +71,214 @@ string VString::downcase(const string& str) { } return out; } + +//###################################################################### +// VHashSha1 + +static inline uint32_t sha1Rotl32(uint32_t lhs, uint32_t rhs) VL_ATTR_ALWINLINE; +static inline uint32_t sha1Rotl32(uint32_t lhs, uint32_t rhs) { + return ((lhs << rhs) | (lhs >> (32 - rhs))); +} + +static inline void sha1Block(uint32_t* h, uint32_t* w) VL_ATTR_ALWINLINE; +static inline void sha1Block(uint32_t* h, uint32_t* w) { +#define SHA1ITER(func, roundConst) do { \ + uint32_t t = sha1Rotl32(a, 5) + (func) + e + (roundConst) + w[round]; \ + e = d; d = c; c = sha1Rotl32(b, 30); b = a; a = t; \ + } while (0) + + uint32_t a = h[0]; + uint32_t b = h[1]; + uint32_t c = h[2]; + uint32_t d = h[3]; + uint32_t e = h[4]; + int round = 0; + + for (; round < 16; ++round) { + SHA1ITER((b & c) | (~b & d), 0x5a827999); + } + for (; round < 20; ++round) { + w[round] = sha1Rotl32((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + SHA1ITER((b & c) | (~b & d), 0x5a827999); + } + for (; round < 40; ++round) { + w[round] = sha1Rotl32((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + SHA1ITER(b ^ c ^ d, 0x6ed9eba1); + } + for (; round < 60; ++round) { + w[round] = sha1Rotl32((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + SHA1ITER((b & c) | (b & d) | (c & d), 0x8f1bbcdc); + } + for (; round < 80; ++round) { + w[round] = sha1Rotl32((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + SHA1ITER(b ^ c ^ d, 0xca62c1d6); + } + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + h[4] += e; +#undef SHA1ITER +} + +void VHashSha1::insert(const void* datap, size_t length) { + UASSERT(!m_final, "Called VHashSha1::insert after finalized the hash value"); + m_totLength += length; + + string tempData; + int chunkLen; + const uint8_t* chunkp; + if (m_remainder=="") { + chunkLen = length; + chunkp = (const uint8_t*)datap; + } else { + // If there are large inserts it would be more efficient to avoid this copy + // by copying bytes in the loop below from either m_remainder or the data + // as appropriate. + tempData = m_remainder + string((const char*)datap,length); + chunkLen = tempData.length(); + chunkp = (const uint8_t*)tempData.data(); + } + + // See wikipedia SHA-1 algorithm summary + uint32_t w[80]; // Round buffer, [0..15] are input data, rest used by rounds + int posBegin = 0; // Position in buffer for start of this block + int posEnd = 0; // Position in buffer for end of this block + + // Process complete 64-byte blocks + while (posBegin <= chunkLen - 64) { + posEnd = posBegin + 64; + // 64 byte round input data, being careful to swap on big, keep on little + for (int roundByte = 0; posBegin < posEnd; posBegin += 4) { + w[roundByte++] = ((uint32_t) chunkp[posBegin + 3] + | (((uint32_t) chunkp[posBegin + 2]) << 8) + | (((uint32_t) chunkp[posBegin + 1]) << 16) + | (((uint32_t) chunkp[posBegin]) << 24)); + } + sha1Block(m_inthash, w); + } + + m_remainder = string((const char*)(chunkp+posBegin), chunkLen-posEnd); +} + +void VHashSha1::finalize() { + if (!m_final) { + // Make sure no 64 byte blocks left + insert(""); + m_final = true; + + // Process final possibly non-complete 64-byte block + uint32_t w[80]; // Round buffer, [0..15] are input data, rest used by rounds + for (int i=0; i<16; ++i) w[i] = 0; + size_t blockPos = 0; + for (; blockPos < m_remainder.length(); ++blockPos) { + w[blockPos >> 2] |= ((uint32_t) m_remainder[blockPos]) << ((3 - (blockPos & 3)) << 3); + } + w[blockPos >> 2] |= 0x80 << ((3 - (blockPos & 3)) << 3); + if (m_remainder.length() >= 56) { + sha1Block(m_inthash, w); + for (int i=0; i<16; ++i) w[i] = 0; + } + w[15] = m_totLength << 3; + sha1Block(m_inthash, w); + + m_remainder.clear(); + } +} + +string VHashSha1::digestBinary() { + finalize(); + string out; out.reserve(20); + for (size_t i=0; i<20; ++i) { + out[i] = (m_inthash[i >> 2] >> (((3 - i) & 0x3) << 3)) & 0xff; + } + return out; +} + +uint64_t VHashSha1::digestUInt64() { + const string& binhash = digestBinary(); + uint64_t out = 0; + for (size_t byte=0; byte>4) & 0xf ]; + out += digits[ (binhash[byte]>>0) & 0xf ]; + } + return out; +} + +string VHashSha1::digestSymbol() { + // Make a symbol name from hash. Similar to base64, however base 64 + // has + and / for last two digits, but need C symbol, and we also + // avoid conflicts with use of _, so use "AB" at the end. + // Thus this function is non-reversable. + static const char digits[64+1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"; + const string& binhash = digestBinary(); + string out; out.reserve(28); + int pos = 0; + for (; pos < (160/8) - 2; pos += 3) { + out += digits[((binhash[pos] >> 2) & 0x3f)]; + out += digits[((binhash[pos] & 0x3) << 4) + | ((int) (binhash[pos + 1] & 0xf0) >> 4)]; + out += digits[((binhash[pos + 1] & 0xf) << 2) + | ((int) (binhash[pos + 2] & 0xc0) >> 6)]; + out += digits[((binhash[pos + 2] & 0x3f))]; + } + if (0) { // Not needed for 160 bit hash + out += digits[((binhash[pos] >> 2) & 0x3f)]; + out += digits[((binhash[pos] & 0x3) << 4)]; + } + else { + out += digits[((binhash[pos] >> 2) & 0x3f)]; + out += digits[((binhash[pos] & 0x3) << 4) + | ((int) (binhash[pos + 1] & 0xf0) >> 4)]; + out += digits[((binhash[pos + 1] & 0xf) << 2)]; + } + return out; +} + +void VHashSha1::selfTestOne(const string& data, const string& data2, + const string& exp, const string& exp64) { + VHashSha1 digest (data); + if (data2!="") digest.insert(data2); + if (digest.digestHex() != exp) { + cerr << "%Error: When hashing '"< +#include "V3Error.h" //###################################################################### // VString - String manipulation @@ -37,53 +38,47 @@ public: }; //###################################################################### -// Compute FNV1a (Fowler/Noll/Vo) hashes -// See http://www.isthe.com/chongo/tech/comp/fnv/index.html -// Algorithmic basis for these functions was in the public domain, by chongo +// VHashSha1 - Compute Sha1 hashes -class VHashFnv { - enum { FNV1_64_INIT = 0xcbf29ce484222325ULL }; // Initial value - vluint64_t m_hash; +class VHashSha1 { + // As blocks must be processed in 64 byte chunks, this does not at present + // support calling input() on multiple non-64B chunks and getting the correct + // hash. To do that first combine the string before calling here. + // Or improve to store 0-63 bytes of data between calls to input(). - inline void hashC(uint8_t c) { - m_hash ^= c; - // Below is faster than m_hash *= 0x100000001b3ULL; - m_hash += ((m_hash << 1) + (m_hash << 4) + (m_hash << 5) - + (m_hash << 7) + (m_hash << 8) + (m_hash << 40)); - } + // MEMBERS + uint32_t m_inthash[5]; // Intermediate hash, in host order + string m_remainder; // Unhashed data + bool m_final; // Finalized + size_t m_totLength; // Total all-chunk length as needed by output digest public: - VHashFnv() : m_hash(FNV1_64_INIT) {} - ~VHashFnv() {} + // CONSTRUCTORS + VHashSha1() { init(); } + VHashSha1(const string& data) { init(); insert(data); } + ~VHashSha1() {} + // METHODS + + string digestBinary(); // Return digest as 20 character binary + string digestHex(); // Return digest formatted as a hex string + string digestSymbol(); // Return digest formatted as C symbol/base64ish + uint64_t digestUInt64(); // Return 64-bits of digest + static void selfTest(); // Test this class - vluint64_t digestUInt64() const { return m_hash; } + // Inerting hash data + void insert(const void* datap, size_t length); // Process data into the digest + void insert(const string& data) { insert(data.data(), data.length()); } // Process data into the digest + void insert(uint64_t value) { insert(cvtToStr(value)); } - VHashFnv& insert(const void* bufp, size_t len) { // Memory - const uint8_t* bp = (const uint8_t*)bufp; - const uint8_t* be = bp + len; - while (bp < be) hashC((vluint64_t)*bp++); - return *this; +private: + void init() { + m_inthash[0] = 0x67452301; m_inthash[1] = 0xefcdab89; m_inthash[2] = 0x98badcfe; + m_inthash[3] = 0x10325476; m_inthash[4] = 0xc3d2e1f0; + m_final = false; + m_totLength = 0; } - VHashFnv& insert(const char* strp) { // String - const uint8_t* sp = (const uint8_t*)strp; - while (*sp) hashC((vluint64_t)*sp++); - return *this; - } - VHashFnv& insert(const string& str) { return insert(str.data(), str.length()); } - VHashFnv& insert(vluint64_t n) { - hashC(n>>0); hashC(n>>8); hashC(n>>16); hashC(n>>24); - hashC(n>>32); hashC(n>>40); hashC(n>>48); hashC(n>>56); - return *this; - } - VHashFnv& insert(uint32_t n) { - hashC(n>>0); hashC(n>>8); hashC(n>>16); hashC(n>>24); - return *this; - } - VHashFnv& insert(uint16_t n) { - hashC(n>>0); hashC(n>>8); - return *this; - } - VHashFnv& insert(uint8_t n) { hashC(n); return *this; } - VHashFnv& insert(int n) { hashC((vluint64_t)n); return *this; } + static void selfTestOne(const string& data, const string& data2, + const string& exp, const string& exp64); + void finalize(); // Process remaining data }; //###################################################################### diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 61a5791f9..6a820c2d6 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -79,6 +79,7 @@ #include "V3Split.h" #include "V3SplitAs.h" #include "V3Stats.h" +#include "V3String.h" #include "V3Subst.h" #include "V3Table.h" #include "V3Task.h" @@ -576,6 +577,7 @@ int main(int argc, char** argv, char** env) { } // Internal tests (after option parsing as need debug() setting) + VHashSha1::selfTest(); AstBasicDTypeKwd::test(); V3Graph::test();