Internals: Replace FNV hashes with SHA1

This commit is contained in:
Wilson Snyder 2015-09-19 18:49:54 -04:00
parent 7163c8d048
commit 64748b7b1d
4 changed files with 251 additions and 42 deletions

View File

@ -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());

View File

@ -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<sizeof(uint64_t); ++byte) {
out = (out<<8) | binhash[byte];
}
return out;
}
string VHashSha1::digestHex() {
static const char digits[16+1] = "0123456789abcdef";
const string& binhash = digestBinary();
string out; out.reserve(40);
for (size_t byte=0; byte<20; ++byte) {
out += digits[ (binhash[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 '"<<data+data2<<"'"<<endl;
cerr << "%Error: got="<<digest.digestHex()<<endl;
cerr << "%Error: exp="<<exp<<endl;
}
if (digest.digestSymbol() != exp64) {
cerr << "%Error: When hashing '"<<data+data2<<"'"<<endl;
cerr << "%Error: got="<<digest.digestSymbol()<<endl;
cerr << "%Error: exp="<<exp64<<endl;
}
}
void VHashSha1::selfTest() {
selfTestOne("", "",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"2jmj7l5rSw0yVbBvlWAYkKBYBwk");
selfTestOne("a", "",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"hvfkNBqlpBzhXR3cuerq6jd2Z7g");
selfTestOne("The quick brown fox jumps over the lazy dog", "",
"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
"L9ThxnotKPzthJ7hu3bnORuT6xI");
selfTestOne("The quick brown fox jumps over the lazy"," dog",
"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
"L9ThxnotKPzthJ7hu3bnORuT6xI");
selfTestOne("Test using larger than block-size key and larger than one block-size data", "",
"9026e8faed6ef4ec5ae3ff049020d7f0af7abbbf",
"kCboAu1u9Oxa4B8EkCDX8K96u78");
selfTestOne("Test using", " larger than block-size key and larger than one block-size data",
"9026e8faed6ef4ec5ae3ff049020d7f0af7abbbf",
"kCboAu1u9Oxa4B8EkCDX8K96u78");
}

View File

@ -24,6 +24,7 @@
#include "config_build.h"
#include "verilatedos.h"
#include <string>
#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 <Landon Curt Noll>
// 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
};
//######################################################################

View File

@ -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();