BOOL CPwCompatImpl::OpenDatabaseV1(CPwManager* pMgr, const TCHAR *pszFile) { FILE *fp; char *pVirtualFile; unsigned long uFileSize, uAllocated, uEncryptedPartSize; size_t i, j, pos; PW_DBHEADER_V1 hdr; sha256_ctx sha32; UINT8 uFinalKey[32]; DWORD dw, dw2; char *ptrTemp; char *ptrTitle; char *ptrUserName; char *ptrURL; char *ptrPassword; char *ptrAdditional; PW_TIME tNow; PW_GROUP pwTG; PW_ENTRY pwTE; _GetCurrentPwTime(&tNow); memset(&pwTG, 0, sizeof(PW_GROUP)); memset(&pwTE, 0, sizeof(PW_ENTRY)); pwTG.tCreation = tNow; pwTG.tExpire = g_pwTimeNever; pwTG.tLastAccess = tNow; pwTG.tLastMod = tNow; pwTE.tCreation = tNow; pwTE.tExpire = g_pwTimeNever; pwTE.tLastAccess = tNow; pwTE.tLastMod = tNow; ASSERT(sizeof(char) == 1); ASSERT(pszFile != NULL); if(pszFile == NULL) return FALSE; ASSERT(_tcslen(pszFile) != 0); if(_tcslen(pszFile) == 0) return FALSE; fp = NULL; _tfopen_s(&fp, pszFile, _T("rb")); if(fp == NULL) return FALSE; // Get file size fseek(fp, 0, SEEK_END); uFileSize = ftell(fp); fseek(fp, 0, SEEK_SET); if(uFileSize < sizeof(PW_DBHEADER_V1)) { fclose(fp); return FALSE; } // Allocate enough memory to hold the complete file uAllocated = uFileSize + 17; pVirtualFile = new char[uAllocated]; if(pVirtualFile == NULL) { fclose(fp); return FALSE; } pVirtualFile[uFileSize + 17 - 1] = 0; fread(pVirtualFile, 1, uFileSize, fp); fclose(fp); // Extract header structure from memory file memcpy(&hdr, pVirtualFile, sizeof(PW_DBHEADER_V1)); // Check if we can open this if((hdr.dwSignature1 != PWM_DBSIG_1) || (hdr.dwSignature2 != PWM_DBSIG_2)) { _OPENDB_FAIL; } if((hdr.dwFlags & PWM_FLAG_RIJNDAEL) != 0) pMgr->SetAlgorithm(ALGO_AES); else if((hdr.dwFlags & PWM_FLAG_TWOFISH) != 0) pMgr->SetAlgorithm(ALGO_TWOFISH); else { ASSERT(FALSE); _OPENDB_FAIL; } BYTE aMasterKeyU[32]; pMgr->GetRawMasterKey(aMasterKeyU); // Hash the master password with the salt in the file sha256_begin(&sha32); sha256_hash(hdr.aMasterSeed, 16, &sha32); sha256_hash(aMasterKeyU, 32, &sha32); sha256_end((unsigned char *)uFinalKey, &sha32); mem_erase(aMasterKeyU, 32); if(pMgr->GetAlgorithm() == ALGO_AES) { CRijndael aes; // Initialize Rijndael algorithm if(aes.Init(CRijndael::CBC, CRijndael::DecryptDir, uFinalKey, CRijndael::Key32Bytes, hdr.aEncryptionIV) != RIJNDAEL_SUCCESS) { _OPENDB_FAIL; } // Decrypt! The first 48 bytes aren't encrypted (that's the header) uEncryptedPartSize = (unsigned long)aes.PadDecrypt((UINT8 *)pVirtualFile + 48, uFileSize - 48, (UINT8 *)pVirtualFile + 48); } else if(pMgr->GetAlgorithm() == ALGO_TWOFISH) { CTwofish twofish; if(twofish.Init(uFinalKey, 32, hdr.aEncryptionIV) != true) { _OPENDB_FAIL }; uEncryptedPartSize = (unsigned long)twofish.PadDecrypt( (UINT8 *)pVirtualFile + 48, uFileSize - 48, (UINT8 *)pVirtualFile + 48); }
// 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) { char *pVirtualFile; unsigned long uFileSize, uAllocated, uEncryptedPartSize; unsigned long pos; PW_DBHEADER hdr; sha256_ctx sha32; UINT8 uFinalKey[32]; char *p; USHORT usFieldType; DWORD dwFieldSize; PW_GROUP pwGroupTemplate; PW_ENTRY pwEntryTemplate; BOOST_STATIC_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)); } FILE *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 + 64 + 4; // 16 = encryption buffer space, 1+64 = string terminating NULLs, 4 unused pVirtualFile = new char[uAllocated]; if(pVirtualFile == NULL) { fclose(fp); return PWE_NO_MEM; } memset(&pVirtualFile[uFileSize + 16], 0, 1 + 64); fread(pVirtualFile, 1, uFileSize, fp); fclose(fp); // Extract header structure from memory file memcpy(&hdr, pVirtualFile, sizeof(PW_DBHEADER)); // Check if it's a KDBX file created by KeePass 2.x if((hdr.dwSignature1 == PWM_DBSIG_1_KDBX_P) && (hdr.dwSignature2 == PWM_DBSIG_2_KDBX_P)) { _OPENDB_FAIL_LIGHT; return PWE_UNSUPPORTED_KDBX; } if((hdr.dwSignature1 == PWM_DBSIG_1_KDBX_R) && (hdr.dwSignature2 == PWM_DBSIG_2_KDBX_R)) { _OPENDB_FAIL_LIGHT; return PWE_UNSUPPORTED_KDBX; } // 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 ((CPwCompatImpl::OpenDatabaseV2(this, 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 ((CPwCompatImpl::OpenDatabaseV1(this, pszFile) != FALSE) ? PWE_SUCCESS : PWE_UNKNOWN); } else { ASSERT(FALSE); _OPENDB_FAIL; } } if(hdr.dwGroups == 0) { _OPENDB_FAIL_LIGHT; return PWE_DB_EMPTY; } // 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; } ProtectTransformedMasterKey(false); // 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); ProtectTransformedMasterKey(true); 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 &= ~0xFUL; 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)); }