pwmanager.cpp

来自「一款密码保险箱源码」· C++ 代码 · 共 2,201 行 · 第 1/5 页

CPP
2,201
字号
	ASSERT(pEntry->pszPassword[pEntry->uPasswordLen] == 0);
}

void CPwManager::UnlockEntryPassword(__inout_ecount(1) PW_ENTRY *pEntry)
{
	ASSERT_ENTRY(pEntry);
	LockEntryPassword(pEntry); // OFB encryption mode

#ifdef _DEBUG
	unsigned int i;
	for(i = 0; i < pEntry->uPasswordLen; i++)
	{
		ASSERT(pEntry->pszPassword[i] != 0);
	}
#endif
}

void CPwManager::NewDatabase()
{
	_DeleteEntryList(TRUE); // Delete really everything, the strings too
	_DeleteGroupList(TRUE);

	m_pLastEditedEntry = NULL;

	_AllocGroups(PWM_NUM_INITIAL_GROUPS); // Allocate some space for the new items
	_AllocEntries(PWM_NUM_INITIAL_ENTRIES);

	m_vUnknownMetaStreams.clear();
}

#define _OPENDB_FAIL_LIGHT \
{ \
	if(pVirtualFile != NULL) \
	{ \
		mem_erase((unsigned char *)pVirtualFile, uAllocated); \
		SAFE_DELETE_ARRAY(pVirtualFile); \
	} \
	m_dwKeyEncRounds = PWM_STD_KEYENCROUNDS; \
}
#define _OPENDB_FAIL \
{ \
	_OPENDB_FAIL_LIGHT; \
	SAFE_DELETE_ARRAY(pwGroupTemplate.pszGroupName); \
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszTitle); \
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszURL); \
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszUserName); \
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszPassword); \
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszAdditional); \
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszBinaryDesc); \
	SAFE_DELETE_ARRAY(pwEntryTemplate.pBinaryData); \
	return PWE_INVALID_FILESTRUCTURE; \
}

#define RESET_TIME_FIELD_NORMAL(pTimeEx) { \
	(pTimeEx)->btDay = 1; (pTimeEx)->btHour = 0; (pTimeEx)->btMinute = 0; \
	(pTimeEx)->btMonth = 1; (pTimeEx)->btSecond = 0; (pTimeEx)->shYear = 2004; }
#define RESET_TIME_FIELD_EXPIRE(pTimeEx) { \
	(pTimeEx)->btDay = 28; (pTimeEx)->btHour = 23; (pTimeEx)->btMinute = 59; \
	(pTimeEx)->btMonth = 12; (pTimeEx)->btSecond = 59; (pTimeEx)->shYear = 4092; }

#define RESET_PWG_TEMPLATE(ptrx) { \
	memset(ptrx, 0, sizeof(PW_GROUP)); \
	RESET_TIME_FIELD_NORMAL(&(ptrx)->tCreation); RESET_TIME_FIELD_NORMAL(&(ptrx)->tLastMod); \
	RESET_TIME_FIELD_NORMAL(&(ptrx)->tLastAccess); RESET_TIME_FIELD_EXPIRE(&(ptrx)->tExpire); }
#define RESET_PWE_TEMPLATE(ptrx) { \
	memset(ptrx, 0, sizeof(PW_ENTRY)); \
	RESET_TIME_FIELD_NORMAL(&(ptrx)->tCreation); RESET_TIME_FIELD_NORMAL(&(ptrx)->tLastMod); \
	RESET_TIME_FIELD_NORMAL(&(ptrx)->tLastAccess); RESET_TIME_FIELD_EXPIRE(&(ptrx)->tExpire); }

// int CPwManager::OpenDatabase(const TCHAR *pszFile, __out_opt PWDB_REPAIR_INFO *pRepair)
// {
//	return this->OpenDatabaseEx(pszFile, pRepair, NULL);
// }

// If bIgnoreCorrupted is TRUE the manager will try to ignore all database file
// errors, i.e. try to read as much as possible instead of breaking out at the
// first error.
// To open a file normally, set bIgnoreCorrupted to FALSE (default).
// To open a file in rescue mode, set it to TRUE.
int CPwManager::OpenDatabase(const TCHAR *pszFile, __out_opt PWDB_REPAIR_INFO *pRepair)
{
	FILE *fp;
	char *pVirtualFile;
	unsigned long uFileSize, uAllocated, uEncryptedPartSize;
	unsigned long pos;
	PW_DBHEADER hdr;
	sha256_ctx sha32;
	UINT8 uFinalKey[32];
	char *p;
	char *pStart;
	USHORT usFieldType;
	DWORD dwFieldSize;
	PW_GROUP pwGroupTemplate;
	PW_ENTRY pwEntryTemplate;

	ASSERT(sizeof(char) == 1);

	ASSERT(pszFile != NULL); if(pszFile == NULL) return PWE_INVALID_PARAM;
	ASSERT(pszFile[0] != 0); if(pszFile[0] == 0) return PWE_INVALID_PARAM; // Length != 0

	RESET_PWG_TEMPLATE(&pwGroupTemplate);
	RESET_PWE_TEMPLATE(&pwEntryTemplate);

	if(pRepair != NULL) { ZeroMemory(pRepair, sizeof(PWDB_REPAIR_INFO)); }

	fp = NULL;
	_tfopen_s(&fp, pszFile, _T("rb"));
	if(fp == NULL) return PWE_NOFILEACCESS_READ;

	// Get file size
	fseek(fp, 0, SEEK_END);
	uFileSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	if(uFileSize < sizeof(PW_DBHEADER))
		{ fclose(fp); return PWE_INVALID_FILEHEADER; }

	// Allocate enough memory to hold the complete file
	uAllocated = uFileSize + 16 + 1 + 8 + 4; // 16 = encryption buffer space, 1+8 = string terminating NULL (UTF-8), 4 unused
	pVirtualFile = new char[uAllocated];
	if(pVirtualFile == NULL) { fclose(fp); return PWE_NO_MEM; }
	memset(&pVirtualFile[uFileSize + 17 - 1], 0, 1 + 8);

	fread(pVirtualFile, 1, uFileSize, fp);
	fclose(fp);

	// Extract header structure from memory file
	memcpy(&hdr, pVirtualFile, sizeof(PW_DBHEADER));

	// Check if we can open this
	if((hdr.dwSignature1 != PWM_DBSIG_1) || (hdr.dwSignature2 != PWM_DBSIG_2))
		{ _OPENDB_FAIL_LIGHT; return PWE_INVALID_FILESIGNATURE; }

	if((hdr.dwVersion & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00))
	{
		if((hdr.dwVersion == 0x00020000) || (hdr.dwVersion == 0x00020001) || (hdr.dwVersion == 0x00020002))
		{
			if(pVirtualFile != NULL)
			{
				mem_erase((unsigned char *)pVirtualFile, uAllocated);
				SAFE_DELETE_ARRAY(pVirtualFile);
			}
			return (_OpenDatabaseV2(pszFile) != FALSE) ? PWE_SUCCESS : PWE_UNKNOWN;
		}
		else if(hdr.dwVersion <= 0x00010002)
		{
			if(pVirtualFile != NULL)
			{
				mem_erase((unsigned char *)pVirtualFile, uAllocated);
				SAFE_DELETE_ARRAY(pVirtualFile);
			}
			return (_OpenDatabaseV1(pszFile) != FALSE) ? PWE_SUCCESS : PWE_UNKNOWN;
		}
		else { ASSERT(FALSE); _OPENDB_FAIL; }
	}

	// Select algorithm
	if((hdr.dwFlags & PWM_FLAG_RIJNDAEL) != 0) m_nAlgorithm = ALGO_AES;
	else if((hdr.dwFlags & PWM_FLAG_TWOFISH) != 0) m_nAlgorithm = ALGO_TWOFISH;
	else { ASSERT(FALSE); _OPENDB_FAIL; }

	m_dwKeyEncRounds = hdr.dwKeyEncRounds;

	// Generate m_pTransformedMasterKey from m_pMasterKey
	if(_TransformMasterKey(hdr.aMasterSeed2) == FALSE) { ASSERT(FALSE); _OPENDB_FAIL; }

	// Hash the master password with the salt in the file
	sha256_begin(&sha32);
	sha256_hash(hdr.aMasterSeed, 16, &sha32);
	sha256_hash(m_pTransformedMasterKey, 32, &sha32);
	sha256_end((unsigned char *)uFinalKey, &sha32);

	if(pRepair == NULL)
	{
		// ASSERT(((uFileSize - sizeof(PW_DBHEADER)) % 16) == 0);
		if(((uFileSize - sizeof(PW_DBHEADER)) % 16) != 0)
		{
			_OPENDB_FAIL_LIGHT;
			return PWE_INVALID_FILESIZE;
		}
	}
	else // Repair the database
	{
		if(((uFileSize - sizeof(PW_DBHEADER)) % 16) != 0)
		{
			uFileSize -= sizeof(PW_DBHEADER); ASSERT((uFileSize & 0xF) != 0);
			uFileSize &= ~0xF;
			uFileSize += sizeof(PW_DBHEADER);
		}

		ASSERT(((uFileSize - sizeof(PW_DBHEADER)) % 16) == 0);

		pRepair->dwOriginalGroupCount = hdr.dwGroups;
		pRepair->dwOriginalEntryCount = hdr.dwEntries;
	}

	if(m_nAlgorithm == ALGO_AES)
	{
		CRijndael aes;

		// Initialize Rijndael algorithm
		if(aes.Init(CRijndael::CBC, CRijndael::DecryptDir, uFinalKey,
			CRijndael::Key32Bytes, hdr.aEncryptionIV) != RIJNDAEL_SUCCESS)
			{ _OPENDB_FAIL_LIGHT; return PWE_CRYPT_ERROR; }

		// Decrypt! The first bytes aren't encrypted (that's the header)
		uEncryptedPartSize = (unsigned long)aes.PadDecrypt((UINT8 *)pVirtualFile + sizeof(PW_DBHEADER),
			uFileSize - sizeof(PW_DBHEADER), (UINT8 *)pVirtualFile + sizeof(PW_DBHEADER));
	}
	else if(m_nAlgorithm == ALGO_TWOFISH)
	{
		CTwofish twofish;

		if(twofish.Init(uFinalKey, 32, hdr.aEncryptionIV) != true)
			{ _OPENDB_FAIL };

		uEncryptedPartSize = (unsigned long)twofish.PadDecrypt((UINT8 *)pVirtualFile + sizeof(PW_DBHEADER),
			uFileSize - sizeof(PW_DBHEADER), (UINT8 *)pVirtualFile + sizeof(PW_DBHEADER));
	}
	else
	{
		ASSERT(FALSE); _OPENDB_FAIL; // This should never happen
	}

#if 0
	// For debugging purposes, a file containing the plain text is created.
	// This code of course must not be compiled into the final binary.
#pragma message("PLAIN TEXT OUTPUT IS ENABLED!")
#pragma message("DO NOT DISTRIBUTE THIS BINARY!")
	// std::basic_string<TCHAR> tstrClear = pszFile;
	// tstrClear += _T(".plaintext.bin");
	// FILE *fpClear = NULL;
	// _tfopen_s(&fpClear, tstrClear.c_str(), _T("wb"));
	// fwrite(pVirtualFile, 1, uFileSize, fpClear);
	// fclose(fpClear); fpClear = NULL;
#endif

	// Check for success (non-repair mode only)
	if(pRepair == NULL)
	{
		if((uEncryptedPartSize > 2147483446) || ((uEncryptedPartSize == 0) &&
			((hdr.dwGroups != 0) || (hdr.dwEntries != 0))))
		{
			_OPENDB_FAIL_LIGHT;
			return PWE_INVALID_KEY;
		}
	}

	// Check if key is correct (with very high probability)
	if(pRepair == NULL)
	{
		sha256_begin(&sha32);
		sha256_hash((unsigned char *)pVirtualFile + sizeof(PW_DBHEADER), uEncryptedPartSize, &sha32);
		sha256_end((unsigned char *)uFinalKey, &sha32);
		if(memcmp(hdr.aContentsHash, uFinalKey, 32) != 0)
			{ _OPENDB_FAIL_LIGHT; return PWE_INVALID_KEY; }
	}

	NewDatabase(); // Create a new database and initialize internal structures

	// Add groups from the memory file to the internal structures
	unsigned long uCurGroup;
	BOOL bRet;
	pos = sizeof(PW_DBHEADER);
	pStart = &pVirtualFile[pos];
	for(uCurGroup = 0; uCurGroup < hdr.dwGroups; )
	{
		p = &pVirtualFile[pos];

		if(pRepair != NULL) if(IsBadReadPtr(p, 2) != FALSE) { _OPENDB_FAIL; }
		memcpy(&usFieldType, p, 2);
		p += 2; pos += 2;
		if(pos >= uFileSize) { _OPENDB_FAIL; }

		if(pRepair != NULL) if(IsBadReadPtr(p, 4) != FALSE) { _OPENDB_FAIL; }
		memcpy(&dwFieldSize, p, 4);
		p += 4; pos += 4;
		if(pos >= (uFileSize + dwFieldSize)) { _OPENDB_FAIL; }

		if(pRepair != NULL) if(IsBadReadPtr(p, dwFieldSize) != FALSE) { _OPENDB_FAIL; }
		bRet = ReadGroupField(usFieldType, dwFieldSize, (BYTE *)p, &pwGroupTemplate);
		if((usFieldType == 0xFFFF) && (bRet == TRUE))
			uCurGroup++; // Now and ONLY now the counter gets increased

		p += dwFieldSize;
		if(p < pStart) { _OPENDB_FAIL; }
		pos += dwFieldSize;
		if(pos >= uFileSize) { _OPENDB_FAIL; }
	}
	SAFE_DELETE_ARRAY(pwGroupTemplate.pszGroupName);

	// Get the entries
	unsigned long uCurEntry;
	for(uCurEntry = 0; uCurEntry < hdr.dwEntries; )
	{
		p = &pVirtualFile[pos];

		if(pRepair != NULL) if(IsBadReadPtr(p, 2) != FALSE) { _OPENDB_FAIL; }
		memcpy(&usFieldType, p, 2);
		p += 2; pos += 2;
		if(pos >= uFileSize) { _OPENDB_FAIL; }

		if(pRepair != NULL) if(IsBadReadPtr(p, 4) != FALSE) { _OPENDB_FAIL; }
		memcpy(&dwFieldSize, p, 4);
		p += 4; pos += 4;
		if(pos >= (uFileSize + dwFieldSize)) { _OPENDB_FAIL; }

		if(pRepair != NULL) if(IsBadReadPtr(p, dwFieldSize) != FALSE) { _OPENDB_FAIL; }
		bRet = ReadEntryField(usFieldType, dwFieldSize, (BYTE *)p, &pwEntryTemplate);
		if((usFieldType == 0xFFFF) && (bRet == TRUE))
			uCurEntry++; // Now and ONLY now the counter gets increased

		p += dwFieldSize;
		if(p < pStart) { _OPENDB_FAIL; }
		pos += dwFieldSize;
		if(pos >= uFileSize) { _OPENDB_FAIL; }
	}
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszTitle);
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszURL);
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszUserName);
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszPassword);
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszAdditional);
	SAFE_DELETE_ARRAY(pwEntryTemplate.pszBinaryDesc);
	SAFE_DELETE_ARRAY(pwEntryTemplate.pBinaryData);

	memcpy(&m_dbLastHeader, &hdr, sizeof(PW_DBHEADER));

	// Erase and delete memory file
	mem_erase((unsigned char *)pVirtualFile, uAllocated);
	SAFE_DELETE_ARRAY(pVirtualFile);

	DWORD dwRemovedStreams = _LoadAndRemoveAllMetaStreams(true);
	if(pRepair != NULL) pRepair->dwRecognizedMetaStreamCount = dwRemovedStreams;
	VERIFY(DeleteLostEntries() == 0);
	FixGroupTree();

	return PWE_SUCCESS;
}

int CPwManager::SaveDatabase(const TCHAR *pszFile)
{
	FILE *fp;
	char *pVirtualFile;
	DWORD uFileSize, uEncryptedPartSize, uAllocated;
	DWORD i, pos;
	PW_DBHEADER hdr;
	UINT8 uFinalKey[32];
	sha256_ctx sha32;
	USHORT usFieldType;
	DWORD dwFieldSize;
	BYTE aCompressedTime[5];

	ASSERT(pszFile != NULL);
	if(pszFile == NULL) return PWE_INVALID_PARAM;
	ASSERT(_tcslen(pszFile) != 0);
	if(_tcslen(pszFile) == 0) return PWE_INVALID_PARAM;

	_AddAllMetaStreams();

	uFileSize = sizeof(PW_DBHEADER);

	BYTE *pbt;

	// Get the size of all groups
	for(i = 0; i < m_dwNumGroups; i++)
	{
		uFileSize += 94; // 6+4+6+6+5+6+5+6+5+6+5+6+4+6+6+2+6+4 = 94

		pbt = _StringToUTF8(m_pGroups[i].pszGroupName);
		uFileSize += szlen((char *)pbt) + 1;
		SAFE_DELETE_ARRAY(pbt);
	}

	// Get the size of all entries together
	for(i = 0; i < m_dwNumEntries; i++)
	{
		ASSERT_ENTRY(&m_pEntries[i]);

		UnlockEntryPassword(&m_pEntries[i]);

		uFileSize += 134; // 6+16+6+4+6+4+6+6+6+6+6+6+5+6+5+6+5+6+5+6 = 122

		pbt = _StringToUTF8(m_pEntries[i].pszTitle);
		uFileSize += szlen((char *)pbt) + 1;
		SAFE_DELETE_ARRAY(pbt);

		pbt = _StringToUTF8(m_pEntries[i].pszUserName);
		uFileSize += szlen((char *)pbt) + 1;
		SAFE_DELETE_ARRAY(pbt);

		pbt = _StringToUTF8(m_pEntries[i].pszURL);
		uFileSize += szlen((char *)pbt) + 1;
		SAFE_DELETE_ARRAY(pbt);

		pbt = _StringToUTF8(m_pEntries[i].pszPassword);
		uFileSize += szlen((char *)pbt) + 1;
		SAFE_DELETE_ARRAY(pbt);

		pbt = _StringToUTF8(m_pEntries[i].pszAdditional);
		uFileSize += szlen((char *)pbt) + 1;
		SAFE_DELETE_ARRAY(pbt);

		pbt = _StringToUTF8(m_pEntries[i].pszBinaryDesc);
		uFileSize += szlen((char *)pbt) + 1;
		SAFE_DELETE_ARRAY(pbt);

		uFileSize += m_pEntries[i].uBinaryDataLen;

		LockEntryPassword(&m_pEntries[i]);
	}

	// Round up filesize to 16-byte boundary for Rijndael/Twofish
	uFileSize = (uFileSize + 16) - (uFileSize % 16);

	// Allocate enough memory
	uAllocated = uFileSize + 16;
	pVirtualFile = new char[uAllocated];
	ASSERT(pVirtualFile != NULL);
	if(pVirtualFile == NULL) { _LoadAndRemoveAllMetaStreams(false); return PWE_NO_MEM; }

	// Build header structure
	hdr.dwSignature1 = PWM_DBSIG_1;
	hdr.dwSignature2 = PWM_DBSIG_2;

	hdr.dwFlags = PWM_FLAG_SHA2; // The one and only hash algorithm available currently

	if(m_nAlgorithm == ALGO_AES) hdr.dwFlags |= PWM_FLAG_RIJNDAEL;
	else if(m_nAlgorithm == ALGO_TWOFISH) hdr.dwFlags |= PWM_FLAG_TWOFISH;
	else { ASSERT(FALSE); _LoadAndRemoveAllMetaStreams(false); return PWE_INVALID_PARAM; }

	hdr.dwVersion = PWM_DBVER_DW;
	hdr.dwGroups = m_dwNumGroups;
	hdr.dwEntries = m_dwNumEntries;
	hdr.dwKeyEncRounds = m_dwKeyEncRounds;

	// Make up the master key hash seed and the encryption IV
	m_random.GetRandomBuffer(hdr.aMasterSeed, 16);
	m_random.GetRandomBuffer((BYTE *)hdr.aEncryptionIV, 16);
	m_random.GetRandomBuffer(hdr.aMasterSeed2, 32);

	// Skip the header, it will be written later

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?