bool CEncryption::Decrypt(const unsigned char* pInput, int nLenInput, const char* szPassword, unsigned char*& pOutput, int& nLenOutput) { bool bResult = false; TD_TLHEADER hdr; RD_UINT8 uFinalKey[32]; sha256_ctx sha32; ASSERT(NULL != pInput); if(NULL == pInput) return FALSE; ASSERT(0 != nLenInput); if(0 == nLenInput) return FALSE; ASSERT(NULL != szPassword); if(NULL == szPassword) return FALSE; ASSERT(sizeof(TD_TLHEADER) <= nLenInput); if(sizeof(TD_TLHEADER) > nLenInput) return FALSE; // Extract header structure from memory file memcpy(&hdr, pInput, sizeof(TD_TLHEADER)); // Hash the header sha256_begin(&sha32); sha256_hash((unsigned char *)&hdr + 32, sizeof(TD_TLHEADER) - 32, &sha32); sha256_end((unsigned char *)uFinalKey, &sha32); // Check if hash of header is the same as stored hash // to verify integrity of header if(0 == memcmp(hdr.aHeaderHash, uFinalKey, 32)) { // Check if we can open this if((hdr.dwSignature1 == TD_TLSIG_1) && (hdr.dwSignature2 == TD_TLSIG_2)) { // Allocate enough memory pOutput = new unsigned char[nLenInput]; if(NULL != pOutput) { unsigned long uKeyLen = strlen(szPassword); // Create MasterKey by hashing szPassword ASSERT(0 != uKeyLen); if(0 != uKeyLen) { sha256_begin(&sha32); sha256_hash((unsigned char *)szPassword, uKeyLen, &sha32); sha256_end(m_pMasterKey, &sha32); m_dwKeyEncRounds = hdr.dwKeyEncRounds; // Generate m_pTransformedMasterKey from m_pMasterKey if(TRUE == _TransformMasterKey(hdr.aMasterSeed2)) { // Hash the master password with the generated hash salt sha256_begin(&sha32); sha256_hash(hdr.aMasterSeed, 16, &sha32); sha256_hash(m_pTransformedMasterKey, 32, &sha32); sha256_end((unsigned char *)uFinalKey, &sha32); bResult = true; } } } } } if (bResult) { bResult = false; Rijndael aes; // Initialize Rijndael/AES if(RIJNDAEL_SUCCESS == aes.init(Rijndael::CBC, Rijndael::Decrypt, uFinalKey, Rijndael::Key32Bytes, hdr.aEncryptionIV) ) { nLenOutput = aes.padDecrypt((RD_UINT8 *)pInput + sizeof(TD_TLHEADER), nLenInput - sizeof(TD_TLHEADER), (RD_UINT8 *)pOutput); // Check if all went correct ASSERT(0 <= nLenOutput); if(0 <= nLenOutput) { // Check contents correct (with high probability) sha256_begin(&sha32); sha256_hash((unsigned char *)pOutput, nLenOutput, &sha32); sha256_end((unsigned char *)uFinalKey, &sha32); if(0 == memcmp(hdr.aContentsHash, uFinalKey, 32)) { bResult = true; // data decrypted successfully } } } } if (!bResult) { SAFE_DELETE_ARRAY(pOutput); } return (bResult); }
bool CEncryption::Encrypt(const unsigned char* pInput, int nLenInput, const char* szPassword, unsigned char*& pOutput, int& nLenOutput) { bool bResult = false; TD_TLHEADER hdr; RD_UINT8 uFinalKey[32]; unsigned long uFileSize = 0, uAllocated = 0, pos = 0; int nEncryptedPartSize = 0; ASSERT(NULL != pInput); if(NULL == pInput) return FALSE; ASSERT(0 != nLenInput); if(0 == nLenInput) return FALSE; ASSERT(NULL != szPassword); if(NULL == szPassword) return FALSE; uFileSize = nLenInput + sizeof(TD_TLHEADER); // Allocate enough memory uAllocated = uFileSize + 16; pOutput = new unsigned char[uAllocated]; if(NULL != pOutput) { unsigned long uKeyLen; // Build header structure hdr.dwSignature1 = TD_TLSIG_1; hdr.dwSignature2 = TD_TLSIG_2; 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); // Create MasterKey by hashing szPassword uKeyLen = strlen(szPassword); ASSERT(0 != uKeyLen); if(0 != uKeyLen) { sha256_ctx sha32; sha256_begin(&sha32); sha256_hash((unsigned char *)szPassword, uKeyLen, &sha32); sha256_end(m_pMasterKey, &sha32); // Generate m_pTransformedMasterKey from m_pMasterKey if(TRUE == _TransformMasterKey(hdr.aMasterSeed2)) { // Hash the master password with the generated hash salt sha256_begin(&sha32); sha256_hash(hdr.aMasterSeed, 16, &sha32); sha256_hash(m_pTransformedMasterKey, 32, &sha32); sha256_end((unsigned char *)uFinalKey, &sha32); // Hash the tasklist contents sha256_begin(&sha32); sha256_hash((unsigned char *)pInput, nLenInput, &sha32); sha256_end((unsigned char *)hdr.aContentsHash, &sha32); // Hash the header sha256_begin(&sha32); sha256_hash((unsigned char *)&hdr + 32, sizeof(TD_TLHEADER) - 32, &sha32); sha256_end((unsigned char *)hdr.aHeaderHash, &sha32); bResult = true; } } } if (bResult) { bResult = false; // Now we have all to build up the header memcpy(pOutput, &hdr, sizeof(TD_TLHEADER)); Rijndael aes; // Initialize Rijndael/AES if(RIJNDAEL_SUCCESS == aes.init(Rijndael::CBC, Rijndael::Encrypt, uFinalKey, Rijndael::Key32Bytes, hdr.aEncryptionIV) ) { nEncryptedPartSize = aes.padEncrypt((RD_UINT8 *)pInput, nLenInput, (RD_UINT8 *)pOutput + sizeof(TD_TLHEADER)); // Check if all went correct ASSERT(0 <= nEncryptedPartSize); if(0 <= nEncryptedPartSize) { bResult = true; // data encrypted successfully } nLenOutput = sizeof(TD_TLHEADER) + nEncryptedPartSize; } } if (!bResult) { SAFE_DELETE_ARRAY(pOutput); } return (bResult); }
// 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)); }