Developers.Hash.TigerTree.Demo.Code
<source lang="c"> // Test function void Test() {
// Use a block size of 1 KB, and a maximum tree height of 9 for all these tests DWORD blocksize, treeheight; blocksize = 1024; // Test the file in 1 KB blocks treeheight = 9; // Don't make any hash trees taller than 9 levels
// Run a series of files through a demonstration of TigerTree hashing and verification TestTiger(_T("C:\\Documents\\a.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\1.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\1a.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\2.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\2a.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\3.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\3a.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\4.txt"), blocksize, treeheight); TestTiger(_T("C:\\Documents\\4a.txt"), blocksize, treeheight); }
// Takes a path to a file on the disk, and the TigerTree block size to use // Shows how TigerTree can has the file and test parts of it void TestTiger(LPCTSTR path, DWORD blocksize, DWORD treeheight) {
// Hash the test file to get its root and tree hashes CBuffer file, tree; // Buffers to hold the data of the file and the whole hash tree CString root; // A string to hold the root hash in base 16 FileToBuffer(path, &file); // Read the contents of the file into a buffer HashTiger(file.m_pBuffer, file.m_nLength, &root, &tree, treeheight); // Hash the whole file, getting the root hash and whole tree
// Make a new TigerTree object and give it the whole tree CTigerTree tiger; // This new TigerTree object doesn't know about the file yet BOOL result; // FromBytes will return a result of true if the tiger tree we are giving it is valid result = tiger.FromBytes( // FromBytes takes a tiger tree from memory and loads it into the object tree.m_pBuffer, // Pointer to the tree tree.m_nLength, // Size of the tree, the number of bytes FromBytes should read there treeheight, // Maximum tree height, Shareaza uses 9 file.m_nLength); // FromBytes also needs to know how big the file is
// Corrupt the middle of the tree and watch TigerTree refuse it tree.m_pBuffer[tree.m_nLength / 2] = tree.m_pBuffer[tree.m_nLength / 2] + 1; // Corrupt the data of the tree CTigerTree badtiger; // We won't use this object after this result = badtiger.FromBytes(tree.m_pBuffer, tree.m_nLength, treeheight, file.m_nLength); // FromBytes returns false
// Start block and blocknumber at the beginning of the file byte* block = file.m_pBuffer; // Point block at the start of the file DWORD blocknumber = 0; // This is the first block, a distance 0 blocks into the file DWORD blockfilled; // How much of this block is filled, useful for the last partially filled block
// Loop while block still points to bytes within the file while (block < file.m_pBuffer + file.m_nLength) {
// Calculate how much of this block is filled blockfilled = file.m_pBuffer + file.m_nLength - block; // The number of bytes in the file beyond the block pointer if (blockfilled > blocksize) blockfilled = blocksize; // If this is more than 1 block, make it just 1 block
// Test the block tiger.BeginBlockTest(); // Tell the TigerTree object we want to test a block of file data tiger.AddToTest(block, blockfilled); // Give AddToTest a pointer to the memory, and the number of bytes there result = tiger.FinishBlockTest(blocknumber); // Tell it what block number that was, it returns true if the block is valid
// Corrupt the block, and see the test fail if (blockfilled >= 5) block[5] = block[5] + 1; // Change the byte 5 bytes into this block in the file tiger.BeginBlockTest(); tiger.AddToTest(block, blockfilled); result = tiger.FinishBlockTest(blocknumber); // The block isn't valid, and FinishBlockTest returns false
// Correct the block, see the test work again if (blockfilled >= 5) block[5] = block[5] - 1; // Change the file byte back to be correct tiger.BeginBlockTest(); tiger.AddToTest(block, blockfilled); result = tiger.FinishBlockTest(blocknumber); // The block is valid again, FinishBlockTest returns true
// Move to the next block block += blocksize; // Move the block pointer forward to the next block in the file blocknumber++; // Record this is one block distance further from the start } }
// Takes a pointer to some memory and the number of bytes of data there // Computes the TigerTree root hash of the information and converts it to and from text void HashTiger(byte* p, DWORD bytes, CString* root, CBuffer* tree, DWORD treeheight) {
// Compute the TigerTree root hash for the file CTigerTree hash; // The object that will compute the hash hash.BeginFile(treeheight, bytes); // Tell it the maximum tree height, and the file size hash.AddToFile(p, bytes); // Give it the data to hash hash.FinishFile(); // That's all
// Get the TigerTree root hash value TIGEROOT value; // The variable that will store the hash value DWORD size = sizeof(value); // 24 bytes hash.GetRoot(&value); // Get the hash value from the object *root = tobase16byte*)(&value), sizeof(value; // Write the root hash as text
// Copy the whole tree into the given buffer DWORD treesize = hash.GetSerialSize(); // The tree will be smaller than this tree->EnsureBuffer(treesize); // Make enough room BOOL result = hash.ToBytes(&(tree->m_pBuffer), &treesize, 9); // Writes how much it wrote in treesize tree->m_nLength += treesize; // Report how much it wrote }
// Takes a path to a file // Opens it, and reads its contents into the given buffer void FileToBuffer(LPCTSTR path, CBuffer* buffer) {
// Open the file and get its size HANDLE file = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); DWORD size = GetFileSize(file, NULL);
// Prepare that much room in the buffer, and read the file into the buffer buffer->EnsureBuffer(size); ReadFile(file, buffer->m_pBuffer, size, &size, NULL); buffer->m_nLength += size;
// Close the file CloseHandle(file); } </source>