diff --git a/development/cmake/CMakeLists.txt b/development/cmake/CMakeLists.txt index 65d1a9a13b..63bf828a9c 100644 --- a/development/cmake/CMakeLists.txt +++ b/development/cmake/CMakeLists.txt @@ -52,6 +52,7 @@ LYX_OPTION(STDLIB_DEBUG "Use debug stdlib" OFF GCC) LYX_OPTION(CONCEPT_CHECKS "Enable concept-checks" OFF GCC) LYX_OPTION(QUIET "Don't generate verbose makefiles" OFF ALL) LYX_OPTION(SHARED_LIBRARIES "Build shared libraries" OFF ALL) +LYX_OPTION(ENCRYPTION "Build with encryption functionality" OFF ALL) message(STATUS) @@ -88,6 +89,9 @@ if(LYX_INSTALL) endif() + + + # Supress regeneration set(CMAKE_SUPPRESS_REGENERATION TRUE) @@ -493,6 +497,12 @@ else() add_subdirectory(boost) endif() +if(LYX_ENCRYPTION) + #Ubuntu: libssl-dev + find_package(OpenSSL REQUIRED) + add_definitions(-DLYX_ENCRYPTION) +endif() + if(NOT LYX_USE_EXTERNAL_LIBINTL) add_subdirectory(intl) diff --git a/development/cmake/config.h.cmake b/development/cmake/config.h.cmake index 4b28a25669..edfc1754fe 100644 --- a/development/cmake/config.h.cmake +++ b/development/cmake/config.h.cmake @@ -20,8 +20,7 @@ #cmakedefine WORDS_BIGENDIAN 1 -#cmakedefine HAVE_ASPELL_ASPELL_H 1 -#cmakedefine HAVE_ASPELL_H 1 + #cmakedefine PACKAGE "${PACKAGE}" #cmakedefine PACKAGE_VERSION "${PACKAGE_VERSION}" @@ -46,6 +45,9 @@ #cmakedefine LYX_USE_TR1_REGEX 1 + + + #endif diff --git a/development/cmake/modules/FindOpenSSL.cmake b/development/cmake/modules/FindOpenSSL.cmake new file mode 100644 index 0000000000..72f72ea946 --- /dev/null +++ b/development/cmake/modules/FindOpenSSL.cmake @@ -0,0 +1,124 @@ +# - Try to find the OpenSSL encryption library +# Once done this will define +# +# OPENSSL_ROOT_DIR - Set this variable to the root installation of OpenSSL +# +# Read-Only variables: +# OPENSSL_FOUND - system has the OpenSSL library +# OPENSSL_INCLUDE_DIR - the OpenSSL include directory +# OPENSSL_LIBRARIES - The libraries needed to use OpenSSL + +#============================================================================= +# Copyright 2006-2009 Kitware, Inc. +# Copyright 2006 Alexander Neundorf +# Copyright 2009-2010 Mathieu Malaterre +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +# http://www.slproweb.com/products/Win32OpenSSL.html +SET(_OPENSSL_ROOT_HINTS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" + ) +SET(_OPENSSL_ROOT_PATHS + "C:/OpenSSL/" + ) +FIND_PATH(OPENSSL_ROOT_DIR + NAMES include/openssl/ssl.h + HINTS ${_OPENSSL_ROOT_HINTS} + PATHS ${_OPENSSL_ROOT_PATHS} +) +MARK_AS_ADVANCED(OPENSSL_ROOT_DIR) + +# Re-use the previous path: +FIND_PATH(OPENSSL_INCLUDE_DIR openssl/ssl.h + ${OPENSSL_ROOT_DIR}/include +) + +IF(WIN32 AND NOT CYGWIN) + # MINGW should go here too + IF(MSVC) + # /MD and /MDd are the standard values - if someone wants to use + # others, the libnames have to change here too + # use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b + # TODO: handle /MT and static lib + # In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix: + # * MD for dynamic-release + # * MDd for dynamic-debug + # * MT for static-release + # * MTd for static-debug + + # Implementation details: + # We are using the libraries located in the VC subdir instead of the parent directory eventhough : + # libeay32MD.lib is identical to ../libeay32.lib, and + # ssleay32MD.lib is identical to ../ssleay32.lib + FIND_LIBRARY(OPENSSL_LIB_EAY_DEBUG NAMES libeay32MDd libeay32 + ${OPENSSL_ROOT_DIR}/lib/VC + ) + FIND_LIBRARY(OPENSSL_LIB_EAY_RELEASE NAMES libeay32MD libeay32 + ${OPENSSL_ROOT_DIR}/lib/VC + ) + FIND_LIBRARY(OPENSSL_SSL_EAY_DEBUG NAMES ssleay32MDd ssleay32 ssl + ${OPENSSL_ROOT_DIR}/lib/VC + ) + FIND_LIBRARY(OPENSSL_SSL_EAY_RELEASE NAMES ssleay32MD ssleay32 ssl + ${OPENSSL_ROOT_DIR}/lib/VC + ) + if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) + set( OPENSSL_LIBRARIES + optimized ${OPENSSL_SSL_EAY_RELEASE} ${OPENSSL_LIB_EAY_RELEASE} + debug ${OPENSSL_SSL_EAY_DEBUG} ${OPENSSL_LIB_EAY_DEBUG} + ) + else() + set( OPENSSL_LIBRARIES ${OPENSSL_SSL_EAY_RELEASE} ${OPENSSL_LIB_EAY_RELEASE} ) + endif() + MARK_AS_ADVANCED(OPENSSL_SSL_EAY_DEBUG OPENSSL_SSL_EAY_RELEASE) + MARK_AS_ADVANCED(OPENSSL_LIB_EAY_DEBUG OPENSSL_LIB_EAY_RELEASE) + ELSEIF(MINGW) + # same player, for MingW + FIND_LIBRARY(OPENSSL_LIB_EAY NAMES libeay32 + ${OPENSSL_ROOT_DIR}/lib/MinGW + ) + FIND_LIBRARY(OPENSSL_SSL_EAY NAMES ssleay32 + ${OPENSSL_ROOT_DIR}/lib/MinGW + ) + MARK_AS_ADVANCED(OPENSSL_SSL_EAY OPENSSL_LIB_EAY) + set( OPENSSL_LIBRARIES ${OPENSSL_SSL_EAY} ${OPENSSL_LIB_EAY} ) + ELSE(MSVC) + # Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues: + FIND_LIBRARY(OPENSSL_LIB_EAY NAMES libeay32 + ${OPENSSL_ROOT_DIR}/lib + ) + FIND_LIBRARY(OPENSSL_SSL_EAY NAMES ssleay32 + ${OPENSSL_ROOT_DIR}/lib + ) + MARK_AS_ADVANCED(OPENSSL_SSL_EAY LIB_EAY) + set( OPENSSL_LIBRARIES ${OPENSSL_SSL_EAY} ${OPENSSL_LIB_EAY} ) + ENDIF(MSVC) +ELSE(WIN32 AND NOT CYGWIN) + + FIND_LIBRARY(OPENSSL_SSL_LIBRARIES NAMES ssl ssleay32 ssleay32MD) + FIND_LIBRARY(OPENSSL_CRYPTO_LIBRARIES NAMES crypto) + MARK_AS_ADVANCED(OPENSSL_CRYPTO_LIBRARIES OPENSSL_SSL_LIBRARIES) + + SET(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES}) + +ENDIF(WIN32 AND NOT CYGWIN) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OpenSSL DEFAULT_MSG + OPENSSL_LIBRARIES + OPENSSL_INCLUDE_DIR +) + +set(OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES} CACHE STRING "OpenSSL libs" FORCE) +MARK_AS_ADVANCED(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES) + diff --git a/development/cmake/src/CMakeLists.txt b/development/cmake/src/CMakeLists.txt index d2f4a90f5c..1c9d135ffb 100644 --- a/development/cmake/src/CMakeLists.txt +++ b/development/cmake/src/CMakeLists.txt @@ -116,7 +116,8 @@ target_link_libraries(${_lyx} ${LIBINTL_LIBRARIES} ${ICONV_LIBRARY} ${QT_QTMAIN_LIBRARY} - ${vld_dll}) + ${vld_dll} + ${OPENSSL_LIBRARIES}) if (HUNSPELL_FOUND) target_link_libraries(${_lyx} ${HUNSPELL_LIBRARY}) diff --git a/development/cmake/src/support/CMakeLists.txt b/development/cmake/src/support/CMakeLists.txt index 4ff45876c8..a98fadfbce 100644 --- a/development/cmake/src/support/CMakeLists.txt +++ b/development/cmake/src/support/CMakeLists.txt @@ -49,7 +49,8 @@ include_directories(${TOP_SRC_DIR}/src/support ${TOP_SRC_DIR}/src/support/mythes ${QT_INCLUDES} ${ICONV_INCLUDE_DIR} - ${ZLIB_INCLUDE_DIR}) + ${ZLIB_INCLUDE_DIR} + ${OPENSSL_INCLUDE_DIR}) if(NOT LYX_MERGE_FILES) @@ -70,7 +71,7 @@ else() ${support_mythes_sources} ${support_linkback_sources} ${support_headers} ${dont_merge}) endif() -target_link_libraries(support ${Lyx_Boost_Libraries} ${QT_QTCORE_LIBRARY} ${ZLIB_LIBRARY}) +target_link_libraries(support ${Lyx_Boost_Libraries} ${QT_QTCORE_LIBRARY} ${ZLIB_LIBRARY} ${OPENSSL_LIBRARIES}) lyx_add_gcc_pch(support) diff --git a/development/cmake/src/tex2lyx/CMakeLists.txt b/development/cmake/src/tex2lyx/CMakeLists.txt index 0058487e0e..56be3be82c 100644 --- a/development/cmake/src/tex2lyx/CMakeLists.txt +++ b/development/cmake/src/tex2lyx/CMakeLists.txt @@ -43,7 +43,8 @@ target_link_libraries(${_tex2lyx} ${Lyx_Boost_Libraries} ${QT_QTCORE_LIBRARY} ${LIBINTL_LIBRARIES} - ${ICONV_LIBRARY}) + ${ICONV_LIBRARY} + ${OPENSSL_LIBRARIES}) if(WIN32) target_link_libraries(${_tex2lyx} shlwapi ole32 psapi) diff --git a/lib/ui/stdmenus.inc b/lib/ui/stdmenus.inc index c5d38a6a79..3edb98bd38 100644 --- a/lib/ui/stdmenus.inc +++ b/lib/ui/stdmenus.inc @@ -49,6 +49,7 @@ Menuset Item "Close All" "buffer-close-all" Item "Save|S" "buffer-write" Item "Save As...|A" "buffer-write-as" +# Item "Save Encrypted" "buffer-write-encrypted" Item "Save All|l" "buffer-write-all" Item "Revert to Saved|R" "buffer-reload" Submenu "Version Control|V" "file_vc" diff --git a/src/FuncCode.h b/src/FuncCode.h index 4b5d94a847..920556094a 100644 --- a/src/FuncCode.h +++ b/src/FuncCode.h @@ -448,7 +448,9 @@ enum FuncCode LFUN_FORWARD_SEARCH, LFUN_INSET_COPY_AS, // vfr, 20100419 LFUN_BUFFER_TOGGLE_OUTPUT_SYNC, + LFUN_BUFFER_WRITE_ENCRYPTED, + // 350 LFUN_LASTACTION // end of the table }; diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp index a731ead1ce..78ac7e5558 100644 --- a/src/LyXAction.cpp +++ b/src/LyXAction.cpp @@ -3090,6 +3090,14 @@ void LyXAction::init() * \endvar */ { LFUN_BUFFER_WRITE, "buffer-write", ReadOnly, Buffer }, +/*! + * \var lyx::FuncCode lyx::LFUN_BUFFER_WRITE_ENCRYPTED + * \li Action: Saves the current buffer encyrpted. + * \li Notion: Saves the current buffer encyrpted to disk, asks for a new filename + * \li Syntax: buffer-write-encyrypted + * \endvar + */ + { LFUN_BUFFER_WRITE_ENCRYPTED, "buffer-write-encrypted", ReadOnly, Buffer }, /*! * \var lyx::FuncCode lyx::LFUN_BUFFER_WRITE_AS * \li Action: Rename and save current buffer. diff --git a/src/frontends/qt4/GuiEncryptionDialog.cpp b/src/frontends/qt4/GuiEncryptionDialog.cpp new file mode 100644 index 0000000000..b2096e7421 --- /dev/null +++ b/src/frontends/qt4/GuiEncryptionDialog.cpp @@ -0,0 +1,64 @@ +// -*- C++ -*- +/** + * \file GuiEncryptionDialog.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Peter Kümmel + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "GuiEncryptionDialog.h" + +#include "qt_i18n.h" +#include "support/qstring_helpers.h" + +#include +#include + + +namespace lyx { +namespace frontend { + + +GuiEncryptionDialog::GuiEncryptionDialog(QWidget *parent) : QDialog(parent) +{ + pwd_label_ = new QLabel(qt_("Password:")); + pwd_edit_ = new QLineEdit; + pwd_edit_->setEchoMode(QLineEdit::Password); + pwd_label_->setBuddy(pwd_edit_); + + button_box_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(button_box_, SIGNAL(accepted()), this, SLOT(accept())); + connect(button_box_, SIGNAL(rejected()), this, SLOT(reject())); + + QHBoxLayout *hLayout = new QHBoxLayout; + hLayout->addWidget(pwd_label_); + hLayout->addWidget(pwd_edit_); + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->setSizeConstraint(QLayout::SetFixedSize); + mainLayout->addLayout(hLayout, 0, 0); + mainLayout->addWidget(button_box_, 1, 0); + setLayout(mainLayout); +} + +QString GuiEncryptionDialog::password() const +{ + return pwd_edit_->text(); +} + +void GuiEncryptionDialog::clearPassword() +{ + pwd_edit_->setText(""); +} + + +} // namespace frontend +} // namespace lyx + +#include "moc_GuiEncryptionDialog.cpp" diff --git a/src/frontends/qt4/GuiEncryptionDialog.h b/src/frontends/qt4/GuiEncryptionDialog.h new file mode 100644 index 0000000000..2fed278802 --- /dev/null +++ b/src/frontends/qt4/GuiEncryptionDialog.h @@ -0,0 +1,47 @@ +// -*- C++ -*- +/** + * \file GuiEncryptionDialog.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Peter Kümmel + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef GUI_ENCRYPTION_DIALOG_H +#define GUI_ENCRYPTION_DIALOG_H + +#include +#include +#include +#include +#include + + +namespace lyx { +namespace frontend { + + +class GuiEncryptionDialog : public QDialog +{ + Q_OBJECT + +public: + GuiEncryptionDialog(QWidget *parent = 0); + + void clearPassword(); + QString password() const; + +private: + QLabel * pwd_label_; + QLineEdit * pwd_edit_; + QDialogButtonBox * button_box_; +}; + + +} // namespace frontend +} // namespace lyx + +#endif + diff --git a/src/frontends/qt4/GuiView.cpp b/src/frontends/qt4/GuiView.cpp index dc569ac049..9338751dc7 100644 --- a/src/frontends/qt4/GuiView.cpp +++ b/src/frontends/qt4/GuiView.cpp @@ -29,7 +29,7 @@ #include "LayoutBox.h" #include "Menus.h" #include "TocModel.h" - +#include "GuiProgress.h" #include "qt_helpers.h" #include "frontends/alert.h" @@ -62,6 +62,7 @@ #include "Text.h" #include "Toolbars.h" #include "version.h" +#include "GuiEncryptionDialog.h" #include "support/convert.h" #include "support/debug.h" @@ -79,7 +80,8 @@ #include "support/Systemcall.h" #include "support/Timeout.h" #include "support/ProgressInterface.h" -#include "GuiProgress.h" +#include "support/CryptographicEncryption.h" + #include #include @@ -106,8 +108,10 @@ #include #include #include - - +#include +#include +#include +#include #define EXPORT_in_THREAD 1 @@ -1480,6 +1484,10 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) break; } + case LFUN_BUFFER_WRITE_ENCRYPTED: + enable = doc_buffer; + break; + case LFUN_BUFFER_WRITE_AS: enable = doc_buffer; break; @@ -1704,11 +1712,71 @@ static FileName selectTemplateFile() return FileName(fromqstr(result.second)); } +/// checks if the file is encrypted and creates a decrypted tmp file +#define LYX_ENC_VERSION 1 +static FileName decryptedFileName(FileName const & filename) +{ + if (!filename.isEncryptedFile()) { + return filename; + } -Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles) + int encVersion = filename.encryptionVersion(); + if (encVersion < 0 || encVersion > LYX_ENC_VERSION) { + Alert::error(_("Encryption"), _("This LyX program is to old to read encrypted file.")); + return filename; + } + + QByteArray encrypted; + QFile file(toqstr(filename.absFileName())); + if (file.open(QIODevice::ReadOnly)) { + encrypted = file.readAll(); + } else { + return filename; + } + + QByteArray key; + CryptographicEncryption enc; + + int keytype = filename.encryptionKeytype(); + if (keytype == CryptographicEncryption::Password) { + GuiEncryptionDialog dlg; + dlg.setWindowTitle(qt_("Enter Password")); + dlg.exec(); + QString pwd = dlg.password(); + key = enc.stringToKey(pwd); + } else { + Alert::error(_("Encryption"), _("Don't know how to generate decryption key.")); + return filename; + } + + // remove the encryption prefix + encrypted.remove(0, FileName::encryptionPrefix(LYX_ENC_VERSION, 1).size()); + + QByteArray decrypted; + if (!enc.decyrpt(encrypted, &decrypted, key)) { + Alert::error(_("Encryption"), _("Error when decryting file.")); + return filename; + } + + // TODO decrypt in memory + // TODO find a better solution than showing the + // generated filename + FileName tempDecrypted = FileName::tempName(); + QFile defile(toqstr(tempDecrypted.absFileName())); + if (defile.open(QIODevice::WriteOnly)) { + defile.write(decrypted); + } + + return tempDecrypted; +} + + +Buffer * GuiView::loadDocument(FileName const & filenameIn, bool tolastfiles) { setBusy(true); + FileName filename = decryptedFileName(filenameIn); + Buffer * newBuffer = checkAndLoadLyXFile(filename); if (!newBuffer) { @@ -2178,7 +2246,7 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname) } -bool GuiView::saveBuffer(Buffer & b) +bool GuiView::saveBuffer(Buffer & b, ostream* stream) { if (workArea(b) && workArea(b)->inDialogMode()) return true; @@ -2186,7 +2254,7 @@ bool GuiView::saveBuffer(Buffer & b) if (b.isUnnamed()) return renameBuffer(b, docstring()); - if (b.save()) { + if ( (stream ? b.write(*stream) : b.save()) ) { theSession().lastFiles().add(b.fileName()); return true; } @@ -2212,10 +2280,126 @@ bool GuiView::saveBuffer(Buffer & b) return false; } - return saveBuffer(b); + return saveBuffer(b, stream); } +bool GuiView::saveBufferEncrypted(Buffer & b) +{ + FileName fname = b.fileName(); + + // Switch to this Buffer. + setBuffer(&b); + + // No argument? Ask user through dialog. + // FIXME UNICODE + FileDialog dlg(qt_("Choose a filename to export document"), + LFUN_BUFFER_WRITE_AS); + dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path)); + dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path)); + + if (!isLyXFileName(fname.absFileName())) + fname.changeExtension(".lyx"); + + FileDialog::Result result = + dlg.save(toqstr(fname.onlyPath().absFileName()), + QStringList(qt_("LyX Documents (*.lyx)")), + toqstr(fname.onlyFileName())); + + if (result.first == FileDialog::Later) + return false; + + fname.set(fromqstr(result.second)); + + if (fname.empty()) + return false; + + if (!isLyXFileName(fname.absFileName())) + fname.changeExtension(".lyx"); + + + // fname is now the new Buffer location. + if (FileName(fname).exists()) { + docstring const file = makeDisplayPath(fname.absFileName(), 30); + docstring text = bformat(_("The document %1$s already " + "exists.\n\nDo you want to " + "overwrite that document?"), + file); + int const ret = Alert::prompt(_("Overwrite document?"), + text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel")); + switch (ret) { + case 0: break; + case 1: break; + case 2: return false; + } + } + + FileName oldauto = b.getAutosaveFileName(); + + // bring the autosave file with us, just in case. + b.moveAutosaveFile(oldauto); + + stringbuf stringBuffer(ios::out|ios::trunc); + ostream stream(&stringBuffer); + if (!saveBuffer(b, &stream)) { + return false; + } + + // the file has now been saved to the new location. + // we need to check that the locations of child buffers + // are still valid. + b.checkChildBuffers(); + + // get password + GuiEncryptionDialog pwddlg; + pwddlg.setWindowTitle(qt_("Enter Password")); + pwddlg.exec(); + if (pwddlg.result() != QDialog::Accepted) + return false; + QString pwd = pwddlg.password(); + + pwddlg.setWindowTitle(qt_("Enter Password again")); + pwddlg.clearPassword(); + pwddlg.exec(); + if (pwddlg.result() != QDialog::Accepted) + return false; + QString pwd2 = pwddlg.password(); + + if (pwd != pwd2) { + Alert::error(_("Password"), _("Passwords do not match")); + return false; + } + + string fileString = stringBuffer.str(); + QByteArray data(fileString.c_str(), fileString.size()); + + CryptographicEncryption enc; + QByteArray key = enc.stringToKey(pwd); + + QByteArray encrypted; + if (!enc.encyrpt(data, &encrypted, key)) { + Alert::error(_("Encryption"), _("Error when encrypting file")); + return false; + } + + // check + QByteArray decrypted; + if (!enc.decyrpt(encrypted, &decrypted, key) || data != decrypted) { + Alert::error(_("Encryption"), _("Error when verifying encrypted file")); + return false; + } + + QString guessStr = toqstr(FileName::encryptionPrefix(LYX_ENC_VERSION, CryptographicEncryption::Password)); + QByteArray bytestoSave = guessStr.toAscii() + encrypted; + + QFile file(toqstr(fname.absFileName())); + if (file.open(QIODevice::WriteOnly)) { + file.write(bytestoSave); + } + + return true; +} + bool GuiView::hideWorkArea(GuiWorkArea * wa) { return closeWorkArea(wa, false); @@ -3060,6 +3244,11 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) saveBuffer(*doc_buffer); break; + case LFUN_BUFFER_WRITE_ENCRYPTED: + LASSERT(doc_buffer, break); + saveBufferEncrypted(*doc_buffer); + break; + case LFUN_BUFFER_WRITE_AS: LASSERT(doc_buffer, break); renameBuffer(*doc_buffer, cmd.argument()); diff --git a/src/frontends/qt4/GuiView.h b/src/frontends/qt4/GuiView.h index f1612b99d7..07abc9f2f6 100644 --- a/src/frontends/qt4/GuiView.h +++ b/src/frontends/qt4/GuiView.h @@ -363,7 +363,9 @@ private: */ bool renameBuffer(Buffer & b, docstring const & newname); /// - bool saveBuffer(Buffer & b); + bool saveBuffer(Buffer & b, std::ostream* stream = 0); + /// Gets a new filename and saves the buffer encrypted + bool saveBufferEncrypted(Buffer & b); /// closes a workarea, if close_buffer is true the buffer will /// also be released, otherwise the buffer will be hidden. bool closeWorkArea(GuiWorkArea * wa, bool close_buffer); diff --git a/src/frontends/qt4/Makefile.am b/src/frontends/qt4/Makefile.am index ba4a3056e0..cf9227d67f 100644 --- a/src/frontends/qt4/Makefile.am +++ b/src/frontends/qt4/Makefile.am @@ -78,6 +78,7 @@ SOURCEFILES = \ GuiDelimiter.cpp \ GuiDialog.cpp \ GuiDocument.cpp \ + GuiEncryptionDialog.cpp \ GuiErrorList.cpp \ GuiERT.cpp \ GuiExternal.cpp \ @@ -188,6 +189,7 @@ MOCHEADER = \ GuiDelimiter.h \ GuiDialog.h \ GuiDocument.h \ + GuiEncryptionDialog.h \ GuiErrorList.h \ GuiERT.h \ GuiExternal.h \ diff --git a/src/support/CryptographicEncryption.cpp b/src/support/CryptographicEncryption.cpp new file mode 100644 index 0000000000..111eb35b31 --- /dev/null +++ b/src/support/CryptographicEncryption.cpp @@ -0,0 +1,182 @@ +// -*- C++ -*- +/** + * \file CryptographicEncryption.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Peter Kümmel + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "CryptographicEncryption.h" + +#include + + +#ifdef LYX_ENCRYPTION +#include +#include +#endif + + +namespace lyx { +namespace support { + + + +CryptographicEncryption::CryptographicEncryption() +{ +} + + +int CryptographicEncryption::blockAlign(int blockSize, QByteArray& bytes) +{ + int pad = 2 * blockSize - (bytes.size() % blockSize); // pad at least one block + bytes.append(QByteArray(pad, (char)pad)); + return pad; +} + + +int CryptographicEncryption::blockDealign(QByteArray& bytes) +{ + int size = bytes.size(); + if (size == 0) + return 0; + char padded = bytes.at(size - 1); + bytes.resize(size - padded); + return padded; +} + + +bool CryptographicEncryption::aesEnryption(QByteArray const & in, QByteArray* out, QByteArray const & key, bool encrypt) +{ +#ifndef LYX_ENCRYPTION + (void) in; + (void) out; + (void) key; + (void) encrypt; + return false; +#else + if (!out) + return false; + + int keySize = key.size(); + if (keySize != 16 && keySize != 24 && keySize != 32) { + return false; + } + + // AES needs aligned data, but we must not touch already encrypted data + QByteArray aligned = in; + if (encrypt) { + blockAlign(AES_BLOCK_SIZE, aligned); + } + if ((aligned.size() % AES_BLOCK_SIZE) != 0) { + return false; + } + + *out = QByteArray(aligned.size(), 0); + AES_KEY aeskey; + if (encrypt) + AES_set_encrypt_key((unsigned char*)key.constData(), keySize * 8, &aeskey); + else + AES_set_decrypt_key((unsigned char*)key.constData(), keySize * 8, &aeskey); + + // use some arbitrary start values + unsigned char iv[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + + AES_cbc_encrypt((unsigned char*)aligned.constData(), + (unsigned char*)out->data(), + aligned.size(), &aeskey, iv, + (encrypt ? AES_ENCRYPT : AES_DECRYPT)); + + if (!encrypt) + blockDealign(*out); + + return true; +#endif +} + + +QByteArray CryptographicEncryption::hash(QByteArray const & bytes, QCryptographicHash::Algorithm algorithm) +{ + QCryptographicHash hashAlgo(algorithm); + hashAlgo.addData(bytes); + return hashAlgo.result(); +} + + +bool CryptographicEncryption::encyrpt(QByteArray const & plain, QByteArray* encrypted, QByteArray const & key) +{ + if (!encrypted) + return false; + + QByteArray bytes; + QDataStream stream(&bytes, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_4_6); + stream << plain; + stream << hash(plain, QCryptographicHash::Md5); + + if (!aesEnryption(bytes, encrypted, key, true)) { + encrypted->clear(); + return false; + } + + return true; +} + + +bool CryptographicEncryption::decyrpt(QByteArray const & encrypted, QByteArray* plain, QByteArray const & key) +{ + if (!plain) + return false; + + QByteArray bytes; + if (!aesEnryption(encrypted, &bytes, key, false)) + return false; + + QByteArray decryptedHash; + QDataStream stream(bytes); + stream.setVersion(QDataStream::Qt_4_6); + stream >> *plain; + stream >> decryptedHash; + + if (decryptedHash != hash(*plain, QCryptographicHash::Md5)) { + plain->clear(); + return false; + } + + return true; +} + + +QByteArray CryptographicEncryption::bytesToKey(QByteArray const & bytes) +{ +#ifndef LYX_ENCRYPTION + (void) bytes; + return QByteArray(); +#else + const char* in = bytes.constData(); + int iterations = 10000; // here we could adjust our paranoija + unsigned char out[64]; + PKCS5_PBKDF2_HMAC_SHA1(in, bytes.size(), 0, 0, iterations, 32, out); + + return QByteArray((const char*) out, 32); +#endif +} + + +QByteArray CryptographicEncryption::stringToKey(QString const & str) +{ + QByteArray utf8 = str.toUtf8(); + return bytesToKey(utf8); +} + + + + + +} +} diff --git a/src/support/CryptographicEncryption.h b/src/support/CryptographicEncryption.h new file mode 100644 index 0000000000..575c834430 --- /dev/null +++ b/src/support/CryptographicEncryption.h @@ -0,0 +1,48 @@ +// -*- C++ -*- +/** + * \file CryptographicEncryption.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Peter Kümmel + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef CRYPTOCRAPHIC_ENCYPTION_H +#define CRYPTOCRAPHIC_ENCYPTION_H + +#include + +namespace lyx { +namespace support { + + +class CryptographicEncryption +{ +public: + CryptographicEncryption(); + + enum Keytype { + Password = 0, + KeytypeCount + }; + + bool encyrpt(QByteArray const & plain, QByteArray * encrypted, QByteArray const & key); + bool decyrpt(QByteArray const & encrypted, QByteArray * plain, QByteArray const & key); + + QByteArray bytesToKey(QByteArray const &); + QByteArray stringToKey(QString const &); + +private: + int blockAlign(int blockSize, QByteArray& bytes); + int blockDealign(QByteArray& bytes); + QByteArray hash(QByteArray const & bytes, QCryptographicHash::Algorithm); + bool aesEnryption(QByteArray const & in, QByteArray* out, QByteArray const & key, bool encrypt); +}; + + +} +} + +#endif diff --git a/src/support/FileName.cpp b/src/support/FileName.cpp index e68d1f7190..e8a8ab997b 100644 --- a/src/support/FileName.cpp +++ b/src/support/FileName.cpp @@ -922,6 +922,12 @@ string FileName::guessFormatFromContents() const else if (contains(str, "BITPIX")) format = "fits"; + + else if (contains(str, encryptionGuessString())) { + string ver = token(str, '-', 1); + string key = token(str, '-', 2); + format = encryptionGuessString() + "-" + ver + "-" + key; + } } // Dia knows also compressed form @@ -946,6 +952,57 @@ bool FileName::isZippedFile() const } +bool FileName::isEncryptedFile() const +{ + string const type = guessFormatFromContents(); + string const guess = encryptionGuessString(); + return toqstr(type).contains(toqstr(guess)); +} + +std::string FileName::encryptionGuessString() +{ + return "LyXEncrypted"; +} + +std::string FileName::encryptionPrefix(int version, int keytype) +{ + // A encrypted file starts with the bytes "LyXEncrypted-001-001-" + // the first number describes the encryption version which could + // change with the time. the second number describes how the key + // is generated, ATM only passwords are supported. + QString guess = toqstr(encryptionGuessString()); + QString vstr = QString::number(version); + QString kstr = QString::number(keytype); + vstr = vstr.rightJustified(3, '0'); + kstr = kstr.rightJustified(3, '0'); + return fromqstr(guess + "-" + vstr + "-" + kstr + "-"); +} + + +int FileName::encryptionVersion() const +{ + string const type = guessFormatFromContents(); + string ver = token(type, '-', 1); + bool ok = false; + int version = toqstr(ver).toInt(&ok); + if (!ok) + return -1; + return version; +} + +int FileName::encryptionKeytype() const +{ + string const type = guessFormatFromContents(); + string ver = token(type, '-', 2); + bool ok = false; + int keytype = toqstr(ver).toInt(&ok); + if (!ok) + return -1; + return keytype; +} + + + docstring const FileName::relPath(string const & path) const { // FIXME UNICODE diff --git a/src/support/FileName.h b/src/support/FileName.h index 23e6fd0d40..8dc58532ed 100644 --- a/src/support/FileName.h +++ b/src/support/FileName.h @@ -178,6 +178,16 @@ public: /// check for zipped file bool isZippedFile() const; + /// check for zipped file + bool isEncryptedFile() const; + /// string which encypted LyX files starts + static std::string encryptionGuessString(); + static std::string encryptionPrefix(int version, int keytype); + /// get version from guessbytes + int encryptionVersion() const; + /// get method how the key is generated + int encryptionKeytype() const; + static FileName fromFilesystemEncoding(std::string const & name); /// (securely) create a temporary file with the given mask. /// \p mask must be in filesystem encoding, if it contains a diff --git a/src/support/Makefile.am b/src/support/Makefile.am index 3c67ecdfa7..e5476619f3 100644 --- a/src/support/Makefile.am +++ b/src/support/Makefile.am @@ -38,6 +38,8 @@ liblyxsupport_a_SOURCES = \ convert.cpp \ convert.h \ copied_ptr.h \ + CryptographicEncryption.h \ + CryptographicEncryption.cpp \ debug.cpp \ debug.h \ docstream.cpp \