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 App::openTree(const QString &file) { NotesFileParser handler; connect(&handler, SIGNAL(backupLoaded(bool, const QString &)), this, SLOT(setBackupLocation(bool, const QString &))); connect(&handler, SIGNAL(notesShowReminderAtStartUpLoaded(bool)), this, SLOT(setNotesShowReminderAtStartUp(bool))); // connect(&handler, SIGNAL(notesWordWrapLoaded(bool)), // this, SLOT(setNotesWordWrap(bool))); // connect(&handler, SIGNAL(notesRootNodeDecorationLoaded(bool)), // this, SLOT(setNotesRootNodeDecoration(bool))); // connect(&handler, SIGNAL(notesShowScrollBarsLoaded(bool)), // this, SLOT(setNotesShowScrollBars(bool))); connect(&handler, SIGNAL(entryLoaded(Entry *)), this, SLOT(addEntry(Entry *))); connect(&handler, SIGNAL(noteLoaded(const QString &, QList<QString> *, const QString &, const QString &, int, QDateTime, const QString &, QDateTime)), notes, SLOT(addNote(const QString &, QList<QString> *, const QString &, const QString &, int, QDateTime, const QString &, QDateTime))); connect(&handler, SIGNAL(securityPasswdLoaded(const QString &)), this, SLOT(setSecurityPasswd(const QString &))); connect(&handler, SIGNAL(noteLoaded(const QString &, Strokes *, const QString &, const QString &, int, QDateTime, const QString &, QDateTime)), notes, SLOT(addNote(const QString &, Strokes *, const QString &, const QString &, int, QDateTime, const QString &, QDateTime))); connect(&handler, SIGNAL(noNoteChild()), notes, SLOT(noNoteChild())); QFile xmlFile(Global::applicationFileName("iqnotes", file + ".xml")), xmlRijndaelFile(Global::applicationFileName("iqnotes", file + ".rijn")); QXmlInputSource *source; // Init xml file if (!xmlFile.exists() && !xmlRijndaelFile.exists()) { xmlFile.open(IO_WriteOnly); QTextStream ts(&xmlFile); ts << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<iqnotes>\n<entries>\n" << "<entry name=\"Company\">\n" << "<property name=\"Name\" type=\"oneLine\"/>\n" << "<property name=\"Address\" type=\"multiLine\"/>\n" << "<property name=\"Tels\" type=\"oneLine\"/>\n" << "<property name=\"Emails\" type=\"oneLine\"/>\n" << "<property name=\"Note\" type=\"multiLine\"/>\n" << "</entry>\n" << "<entry name=\"Person\">\n" << "<property name=\"Name\" type=\"oneLine\"/>\n" << "<property name=\"Nick\" type=\"oneLine\"/>\n" << "<property name=\"Work Address\" type=\"multiLine\"/>\n" << "<property name=\"Work Tel\" type=\"oneLine\"/>\n" << "<property name=\"Work Emails\" type=\"oneLine\"/>\n" << "<property name=\"Home Address\" type=\"multiLine\"/>\n" << "<property name=\"Home Tel\" type=\"oneLine\"/>\n" << "<property name=\"Emails\" type=\"oneLine\"/>\n" << "<property name=\"Note\" type=\"multiLine\"/>\n" << "</entry>\n" << "<entry name=\"User account\">\n" << "<property name=\"Host\" type=\"oneLine\"/>\n" << "<property name=\"Username\" type=\"oneLine\"/>\n" << "<property name=\"Password\" type=\"oneLine\"/>\n" << "<property name=\"Type\" type=\"oneLine\"/>\n" << "<property name=\"Note\" type=\"mutliLine\"/>\n" << "</entry>\n" << "<entry name=\"Credit card\">\n" << "<property name=\"Card type\" type=\"oneLine\"/>\n" << "<property name=\"Account #\" type=\"oneLine\"/>\n" << "<property name=\"Expire\" type=\"oneLine\"/>\n" << "<property name=\"PIN\" type=\"oneLine\"/>\n" << "<property name=\"Note\" type=\"mutliLine\"/>\n" << "</entry>\n" << "<entry name=\"WWW Link\">\n" << "<property name=\"Title\" type=\"oneLine\"/>\n" << "<property name=\"URL\" type=\"oneLine\"/>\n" << "<property name=\"Description\" type=\"multiLine\"/>\n" << "</entry>\n" << "</entries>\n" << "<notes>\n</notes>\n</iqnotes>\n"; xmlFile.close(); source = new QXmlInputSource(xmlFile); } else if (xmlRijndaelFile.exists()) { StartUpPasswdBase passwdD(this, 0, true); passwdD.adjustSize(); if (!passwdD.exec()) { return false; } preferences.setPasswd(passwdD.Passwd->text()); xmlRijndaelFile.open(IO_ReadOnly); uint fileSize = xmlRijndaelFile.size(); unsigned char *rijnData = (unsigned char *) malloc(fileSize), *rijnDataDecrypted = (unsigned char *) malloc(fileSize); Rijndael rijndael; xmlRijndaelFile.readBlock((char *)rijnData, fileSize); rijndael.init(Rijndael::CBC, Rijndael::Decrypt, preferences.passwd16Bin, Rijndael::Key16Bytes); rijndael.padDecrypt(rijnData, fileSize, rijnDataDecrypted); // yeah, stupid if (memcmp((void *)"<iqnotes>", (void *)rijnDataDecrypted, 9) != 0 && memcmp((void *)"<?xml version", (void *)rijnDataDecrypted, 13) != 0) { free(rijnData); free(rijnDataDecrypted); QMessageBox::critical(this, "Bad password", "Please,\ntype correct password."); return false; } source = new QXmlInputSource(); // source->setData(QString((char *)rijnDataDecrypted)); source->setData(QString::fromUtf8((char *)rijnDataDecrypted)); free(rijnData); free(rijnDataDecrypted); } else { source = new QXmlInputSource(xmlFile); } QXmlSimpleReader reader; reader.setContentHandler(&handler); reader.setErrorHandler(new NotesFileParserError); if(!reader.parse(*source)) qWarning(tr("parse error")); delete source; setCaption("IQNotes :: " + file); return true; }
//! \todo Optimize writing section data. Right now it only writes one block at a //! time, which is of course quite slow (in relative terms). //! \todo Refactor this into several different methods for writing each region //! of the image. Use a context structure to keep track of shared data between //! each of the methods. //! \todo Refactor the section and boot tag writing code to only have a single //! copy of the block writing and encryption loop. void EncoreBootImage::writeToStream(std::ostream & stream) { // always generate the session key or DEK even if image is unencrypted m_sessionKey.randomize(); // prepare to compute CBC-MACs with each KEK unsigned i; smart_array_ptr<RijndaelCBCMAC> macs(0); if (isEncrypted()) { macs = new RijndaelCBCMAC[m_keys.size()]; for (i=0; i < m_keys.size(); ++i) { RijndaelCBCMAC mac(m_keys[i]); (macs.get())[i] = mac; } } // prepare to compute SHA-1 digest over entire image CSHA1 hash; hash.Reset(); // count of total blocks written to the file unsigned fileBlocksWritten = 0; // we need some pieces of the header down below boot_image_header_t imageHeader; prepareImageHeader(imageHeader); // write plaintext header { // write header assert(sizeOfPaddingForCipherBlocks(sizeof(boot_image_header_t)) == 0); stream.write(reinterpret_cast<char *>(&imageHeader), sizeof(imageHeader)); fileBlocksWritten += numberOfCipherBlocks(sizeof(imageHeader)); // update CBC-MAC over image header if (isEncrypted()) { for (i=0; i < m_keys.size(); ++i) { (macs.get())[i].update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader)); } } // update SHA-1 hash.Update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader)); } // write plaintext section table { section_iterator_t it = beginSection(); for (; it != endSection(); ++it) { Section * section = *it; // write header for this section assert(sizeOfPaddingForCipherBlocks(sizeof(section_header_t)) == 0); section_header_t sectionHeader; section->fillSectionHeader(sectionHeader); stream.write(reinterpret_cast<char *>(§ionHeader), sizeof(sectionHeader)); fileBlocksWritten += numberOfCipherBlocks(sizeof(sectionHeader)); // update CBC-MAC over this entry if (isEncrypted()) { for (i=0; i < m_keys.size(); ++i) { (macs.get())[i].update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader)); } } // update SHA-1 hash.Update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader)); } } // finished with the CBC-MAC if (isEncrypted()) { for (i=0; i < m_keys.size(); ++i) { (macs.get())[i].finalize(); } } // write key dictionary if (isEncrypted()) { key_iterator_t it = beginKeys(); for (i=0; it != endKeys(); ++it, ++i) { // write CBC-MAC result for this key, then update SHA-1 RijndaelCBCMAC & mac = (macs.get())[i]; const RijndaelCBCMAC::block_t & macResult = mac.getMAC(); stream.write(reinterpret_cast<const char *>(&macResult), sizeof(RijndaelCBCMAC::block_t)); hash.Update(reinterpret_cast<const uint8_t *>(&macResult), sizeof(RijndaelCBCMAC::block_t)); fileBlocksWritten++; // encrypt DEK with this key, write it out, and update image digest Rijndael cipher; cipher.init(Rijndael::CBC, Rijndael::Encrypt, *it, Rijndael::Key16Bytes, imageHeader.m_iv); AESKey<128>::key_t wrappedSessionKey; cipher.blockEncrypt(m_sessionKey, sizeof(AESKey<128>::key_t) * 8, wrappedSessionKey); stream.write(reinterpret_cast<char *>(&wrappedSessionKey), sizeof(wrappedSessionKey)); hash.Update(reinterpret_cast<uint8_t *>(&wrappedSessionKey), sizeof(wrappedSessionKey)); fileBlocksWritten++; } } // write sections and boot tags { section_iterator_t it = beginSection(); for (; it != endSection(); ++it) { section_iterator_t itCopy = it; bool isLastSection = (++itCopy == endSection()); Section * section = *it; cipher_block_t block; unsigned blockCount = section->getBlockCount(); unsigned blocksWritten = 0; Rijndael cipher; cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); // Compute the number of padding blocks needed to align the section. This first // call to getPadBlockCountForOffset() passes an offset that excludes // the boot tag for this section. unsigned paddingBlocks = getPadBlockCountForSection(section, fileBlocksWritten); // Insert nop commands as padding to align the start of the section, if // the section has special alignment requirements. NopCommand nop; while (paddingBlocks--) { blockCount = nop.getBlockCount(); blocksWritten = 0; while (blocksWritten < blockCount) { nop.getBlocks(blocksWritten, 1, &block); if (isEncrypted()) { // re-init after encrypt to update IV cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block); cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block); } stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); blocksWritten++; fileBlocksWritten++; } } // reinit cipher for boot tag cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); // write boot tag TagCommand tag(*section); tag.setLast(isLastSection); if (!isLastSection) { // If this isn't the last section, the tag needs to include any // padding for the next section in its length, otherwise the ROM // won't be able to find the next section's boot tag. unsigned nextSectionOffset = fileBlocksWritten + section->getBlockCount() + 1; tag.setSectionLength(section->getBlockCount() + getPadBlockCountForSection(*itCopy, nextSectionOffset)); } blockCount = tag.getBlockCount(); blocksWritten = 0; while (blocksWritten < blockCount) { tag.getBlocks(blocksWritten, 1, &block); if (isEncrypted()) { // re-init after encrypt to update IV cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block); cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block); } stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); blocksWritten++; fileBlocksWritten++; } // reinit cipher for section data cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); // write section data blockCount = section->getBlockCount(); blocksWritten = 0; while (blocksWritten < blockCount) { section->getBlocks(blocksWritten, 1, &block); // Only encrypt the section contents if the entire boot image is encrypted // and the section doesn't have the "leave unencrypted" flag set. Even if the // section is unencrypted the boot tag will remain encrypted. if (isEncrypted() && !section->getLeaveUnencrypted()) { // re-init after encrypt to update IV cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block); cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block); } stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); blocksWritten++; fileBlocksWritten++; } } } // write SHA-1 digest over entire image { // allocate enough room for digest and bytes to pad out to the next cipher block const unsigned padBytes = sizeOfPaddingForCipherBlocks(sizeof(sha1_digest_t)); unsigned digestBlocksSize = sizeof(sha1_digest_t) + padBytes; smart_array_ptr<uint8_t> digestBlocks = new uint8_t[digestBlocksSize]; hash.Final(); hash.GetHash(digestBlocks.get()); // set the pad bytes to random values RandomNumberGenerator rng; rng.generateBlock(&(digestBlocks.get())[sizeof(sha1_digest_t)], padBytes); // encrypt with session key if (isEncrypted()) { Rijndael cipher; cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); cipher.blockEncrypt(digestBlocks.get(), digestBlocksSize * 8, digestBlocks.get()); } // write to the stream stream.write(reinterpret_cast<char *>(digestBlocks.get()), digestBlocksSize); } }
bool App::saveTree() { // backup dir checking (see also preferences.cpp) if (preferences.saveBackupFile) { QDir backupDir(preferences.backupLocation); if (!backupDir.exists()) { QMessageBox::critical(this, "Invalid directory", "Please, enter the existing\ndirectory in backup location.", 0, 0); return false; } } QFile saveF(Global::applicationFileName("iqnotes", currentFile + ".xml")); QFile rijnF(Global::applicationFileName("iqnotes", currentFile + ".rijn")); QString saveS; QTextStream saveData(saveS, IO_WriteOnly); saveData.setEncoding(saveData.UnicodeUTF8); saveData << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<iqnotes>\n" << "<config>\n" << "<backup save=\"" << (preferences.saveBackupFile ? "yes" : "no") << "\" location=\"" << preferences.backupLocation << "\"/>\n" << "<notes showreminder=\"" << (preferences.showReminder ? "yes" : "no") << "\" />\n" << "<security passwd=\"" << preferences.passwdHex << "\"/>\n" << "</config>\n" << "<entries>\n"; for (Entry *e = entriesList->first(); e; e = entriesList->next()) { saveData << "<entry name=\"" << quoteXML(e->getName()) << "\" defaultpic=\"" << quoteXML(e->getDefaultPic()) << "\">\n"; for (PropertyStruct *ps = e->first(); ps; ps = e->next()) { saveData << "<property name=\"" << quoteXML(ps->getName()) << "\" " << "type=\"" << (ps->getType() == PropertyStruct::ONE_LINE ? "oneLine" : "multiLine") << "\"/>\n"; } saveData << "</entry>\n"; } saveData << "</entries>\n"; notes->saveTree(&saveData); saveData << "</iqnotes>\n"; //SF.close(); QCString dataUTF8 = saveS.utf8(); if (!preferences.passwdHex.length()) { saveF.open(IO_WriteOnly); QTextStream saveData1(&saveF); saveData1.setEncoding(saveData1.UnicodeUTF8); saveData1 << saveS; if (preferences.saveBackupFile) { QFile backupSaveF(preferences.backupLocation + "/" + currentFile + ".xml"); QTextStream saveData2(&backupSaveF); backupSaveF.open(IO_WriteOnly); saveData2 << dataUTF8; } if (rijnF.exists()) rijnF.remove(); } else { Rijndael rijndael; unsigned char *rijnEncrypt = (unsigned char*)malloc(dataUTF8.length() + 16); rijnF.open(IO_WriteOnly); rijndael.init(Rijndael::CBC, Rijndael::Encrypt, preferences.passwd16Bin, Rijndael::Key16Bytes); int len = rijndael.padEncrypt((unsigned char*)(const char*)dataUTF8, dataUTF8.length(), rijnEncrypt); rijnF.writeBlock((const char *)rijnEncrypt, len); if (preferences.saveBackupFile) { QFile rijnBackupF(preferences.backupLocation + "/" + currentFile + ".rijn"); rijnBackupF.open(IO_WriteOnly); rijnBackupF.writeBlock((const char *)rijnEncrypt, len); } free(rijnEncrypt); if (saveF.exists()) saveF.remove(); } return true; }