www.pudn.com > DCPlusPlus-src.zip > CryptoManager.cpp


/*  
 * Copyright (C) 2001-2004 Jacek Sieka, j_s at telia com 
 * 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version. 
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 */ 
 
#include "stdinc.h" 
#include "DCPlusPlus.h" 
 
#include "CryptoManager.h" 
 
#include "BitInputStream.h" 
#include "BitOutputStream.h" 
#include "File.h" 
 
#ifdef _WIN32 
#include "../bzip2/bzlib.h" 
#else 
#include  
#endif 
 
void CryptoManager::decodeBZ2(const u_int8_t* is, size_t sz, string& os) throw (CryptoException) { 
	bz_stream bs = { 0 }; 
 
	if(BZ2_bzDecompressInit(&bs, 0, 0) != BZ_OK) 
		throw(CryptoException(STRING(DECOMPRESSION_ERROR))); 
 
	// We assume that the files aren't compressed more than 2:1...if they are it'll work anyway, 
	// but we'll have to do multiple passes... 
	size_t bufsize = 2*sz; 
	AutoArray buf(bufsize); 
	 
	bs.avail_in = sz; 
	bs.avail_out = bufsize; 
	bs.next_in = (char*)(const_cast(is)); 
	bs.next_out = buf; 
 
	int err; 
 
	os.clear(); 
	 
	while((err = BZ2_bzDecompress(&bs)) == BZ_OK) {  
		if (bs.avail_in == 0 && bs.avail_out > 0) { // error: BZ_UNEXPECTED_EOF  
			BZ2_bzDecompressEnd(&bs);  
			throw CryptoException(STRING(DECOMPRESSION_ERROR));  
		}  
		os.append(buf, bufsize-bs.avail_out);  
		bs.avail_out = bufsize;  
		bs.next_out = buf;  
	}  
 
	if(err == BZ_STREAM_END) 
		os.append(buf, bufsize-bs.avail_out); 
	 
	BZ2_bzDecompressEnd(&bs); 
 
	if(err < 0) { 
		// This was a real error 
		throw CryptoException(STRING(DECOMPRESSION_ERROR));	 
	} 
} 
 
string CryptoManager::keySubst(const u_int8_t* aKey, size_t len, size_t n) { 
	u_int8_t* temp = new u_int8_t[len + n * 10]; 
	 
	size_t j=0; 
	 
	for(size_t i = 0; i> 4) | (v1 << 4)) & 0xff); 
	temp[0] = v1; 
	 
	string::size_type i; 
 
	for(i = 1; i> 4) | (v1 << 4))&0xff); 
		temp[i] = v1; 
		if(isExtra(temp[i])) 
			extra++; 
	} 
	 
	temp[0] = (u_int8_t)(temp[0] ^ temp[aLock.length()-1]); 
	 
	if(isExtra(temp[0])) { 
		extra++; 
	} 
	 
	string tmp = keySubst(temp, aLock.length(), extra); 
	delete[] temp; 
	return tmp; 
} 
 
void CryptoManager::decodeHuffman(const u_int8_t* is, string& os, const size_t len) throw(CryptoException) { 
//	BitInputStream bis; 
	int pos = 0; 
 
	if(len < 11 || is[pos] != 'H' || is[pos+1] != 'E' || !((is[pos+2] == '3') || (is[pos+2] == '0'))) { 
		throw CryptoException(STRING(DECOMPRESSION_ERROR)); 
	} 
	pos+=5; 
 
	int size; 
	size = *(int*)&is[pos]; 
 
	pos+=4; 
 
	dcdebug("Size: %d\n", size); 
	 
	unsigned short treeSize; 
	treeSize = *(unsigned short*)&is[pos]; 
 
	pos+=2; 
 
	if(len < (size_t)(11 + treeSize * 2))  
		throw CryptoException(STRING(DECOMPRESSION_ERROR)); 
	Leaf** leaves = new Leaf*[treeSize]; 
 
	int i; 
	for(i=0; ilen; j++) { 
			try { 
				if(bis.get()) { 
					if(node->right == NULL) 
						node->right = new DecNode(); 
 
					node = node->right; 
				} else { 
					if(node->left == NULL) 
						node->left = new DecNode(); 
 
					node = node->left; 
				} 
			} catch(const BitStreamException&) { 
				throw CryptoException(STRING(DECOMPRESSION_ERROR)); 
			} 
		} 
		node->chr = leaves[i]->chr; 
	} 
	 
	bis.skipToByte(); 
	 
	// We know the size, so no need to use strange STL stuff... 
	AutoArray buf(size+1); 
 
	pos = 0; 
	for(i=0; ichr == -1) { 
			try { 
				if(bis.get()) { 
					node = node->right; 
				} else { 
					node = node->left; 
				} 
			} catch(const BitStreamException&) { 
				throw CryptoException(STRING(DECOMPRESSION_ERROR)); 
			} 
 
			if(node == NULL) { 
				for(i=0; ichr; 
	} 
	buf[pos] = 0; 
	os.assign(buf, size); 
 
	for(i=0; i& aTree) { 
	while(aTree.size() > 1) { 
		// Merge the first two nodes 
		Node* node = new Node(aTree.front(), *(++aTree.begin())); 
		aTree.pop_front(); 
		aTree.pop_front(); 
 
		bool done = false; 
		for(list::iterator i=aTree.begin(); i != aTree.end(); ++i) { 
			if(*node <= *(*i)) { 
				aTree.insert(i, node); 
				done = true; 
				break; 
			} 
		} 
 
		if(!done) 
			aTree.push_back(node); 
 
	} 
} 
 
/** 
 * @todo Make more effective in terms of memory allocations and copies... 
 */ 
void CryptoManager::recurseLookup(vector* table, Node* node, vector& u_int8_ts) { 
	if(node->chr != -1) { 
		table[node->chr] = u_int8_ts; 
		return; 
	} 
 
	vector left = u_int8_ts; 
	vector right = u_int8_ts; 
	 
	left.push_back(0); 
	right.push_back(1); 
 
	recurseLookup(table, node->left, left); 
	recurseLookup(table, node->right, right); 
} 
 
/** 
 * Builds a table over the characters available (for fast lookup). 
 * Stores each character as a set of u_int8_ts with values {0, 1}. 
 */ 
void CryptoManager::buildLookup(vector* table, Node* aRoot) { 
	vector left; 
	vector right; 
 
	left.push_back(0); 
	right.push_back(1); 
 
	recurseLookup(table, aRoot->left, left); 
	recurseLookup(table, aRoot->right, right); 
} 
 
 
struct greaterNode {  
	bool operator() (const Node* a, const Node* b) const {  
		return *a < *b;  
	};  
}; 
 
/** 
 * Encodes a set of data with DC's version of huffman encoding.. 
 * @todo Use real streams maybe? or something else than string (operator[] contains a compare, slow...) 
 */ 
void CryptoManager::encodeHuffman(const string& is, string& os) { 
	 
	// We might as well expect this much data as huffman encoding doesn't go very far... 
	os.reserve(is.size()); 
	if(is.length() == 0) { 
		os.append("HE3\x0d"); 
		 
		// Nada... 
		os.append(7, '\0'); 
		return; 
	} 
	// First, we count all characters 
	u_int8_t csum = 0; 
	int count[256]; 
	memset(count, 0, sizeof(count)); 
	int chars = countChars(is, count, csum); 
 
	// Next, we create a set of nodes and add it to a list, removing all characters that never occur. 
	 
	list nodes; 
 
	int i; 
	for(i=0; i<256; i++) { 
		if(count[i] > 0) { 
			nodes.push_back(new Node(i, count[i])); 
		} 
	} 
 
	nodes.sort(greaterNode()); 
#ifdef _DEBUG 
	for(list::iterator it = nodes.begin(); it != nodes.end(); ++it) dcdebug("%.02x:%d, ", (*it)->chr, (*it)->weight); 
	dcdebug("\n"); 
#endif 
	 
	walkTree(nodes); 
	dcassert(nodes.size() == 1); 
 
	Node* root = nodes.front(); 
	vector lookup[256]; 
	 
	// Build a lookup table for fast character lookups 
	buildLookup(lookup, root); 
	delete root; 
 
	// Reserve some memory to avoid all those copies when appending... 
	os.reserve(is.size() * 3 / 4); 
 
	os.append("HE3\x0d"); 
	 
	// Checksum 
	os.append(1, csum); 
	string::size_type sz = is.size(); 
	os.append((char*)&sz, 4); 
 
	// Character count 
	os.append((char*)&chars, 2); 
 
	// The characters and their bitlengths 
	for(i=0; i<256; i++) { 
		if(count[i] > 0) { 
			os.append(1, (u_int8_t)i); 
			os.append(1, (u_int8_t)lookup[i].size()); 
		} 
	} 
	 
	BitOutputStream bos(os); 
	// The tree itself, ie the bits of each character 
	for(i=0; i<256; i++) { 
		if(count[i] > 0) { 
			bos.put(lookup[i]); 
		} 
	} 
	 
	dcdebug("u_int8_ts: %d\n", os.size()); 
	bos.skipToByte(); 
 
	for(string::size_type j=0; j