]> git.lyx.org Git - features.git/commitdiff
add first version of .lyx file encryption. It's disabled by default and could out...
authorPeter Kümmel <syntheticpp@gmx.net>
Sat, 17 Jul 2010 12:26:11 +0000 (12:26 +0000)
committerPeter Kümmel <syntheticpp@gmx.net>
Sat, 17 Jul 2010 12:26:11 +0000 (12:26 +0000)
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@34931 a592a061-630c-0410-9148-cb99ea01b6c8

19 files changed:
development/cmake/CMakeLists.txt
development/cmake/config.h.cmake
development/cmake/modules/FindOpenSSL.cmake [new file with mode: 0644]
development/cmake/src/CMakeLists.txt
development/cmake/src/support/CMakeLists.txt
development/cmake/src/tex2lyx/CMakeLists.txt
lib/ui/stdmenus.inc
src/FuncCode.h
src/LyXAction.cpp
src/frontends/qt4/GuiEncryptionDialog.cpp [new file with mode: 0644]
src/frontends/qt4/GuiEncryptionDialog.h [new file with mode: 0644]
src/frontends/qt4/GuiView.cpp
src/frontends/qt4/GuiView.h
src/frontends/qt4/Makefile.am
src/support/CryptographicEncryption.cpp [new file with mode: 0644]
src/support/CryptographicEncryption.h [new file with mode: 0644]
src/support/FileName.cpp
src/support/FileName.h
src/support/Makefile.am

index 65d1a9a13b149c91250c2418b60e39ec737e8ee2..63bf828a9c096c8ac91429d6b615da4a738f46d9 100644 (file)
@@ -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)
index 4b28a2566998cd5671e1e8258fb2891460826526..edfc1754febb789af5a2b2ffc840c09d849fb696 100644 (file)
@@ -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 (file)
index 0000000..72f72ea
--- /dev/null
@@ -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 <neundorf@kde.org>
+# Copyright 2009-2010 Mathieu Malaterre <mathieu.malaterre@gmail.com>
+#
+# 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)
+
index d2f4a90f5c1ce077169887af0cbc6ff45170f918..1c9d135ffbe2aa0224bf1ead292de88767f04004 100644 (file)
@@ -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})
index 4ff45876c8e57798941be338258afa06a06c4b95..a98fadfbcea9093f21955b42f2db32bd9fcf1e2b 100644 (file)
@@ -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)
 
index 0058487e0eab6f5cace90d4b8670deec8100ba07..56be3be82ccead6299d8093439908df6c04f0119 100644 (file)
@@ -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)
index c5d38a6a794cdef434bf309a91056a623e6e630c..3edb98bd38e013fee54a871e2c7d3ee3297e6daf 100644 (file)
@@ -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"
index 4b5d94a84770e48848b67cc57a028ff60e411829..920556094ad30c6f2a8c834bb8abab6377b869bc 100644 (file)
@@ -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
 };
 
index a731ead1cef01ff69bef8ba45a4165b13d52b773..78ac7e555840234353ba4a9d2800eb9c6d79b164 100644 (file)
@@ -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 (file)
index 0000000..b2096e7
--- /dev/null
@@ -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 <config.h>
+
+#include "GuiEncryptionDialog.h"
+
+#include "qt_i18n.h"
+#include "support/qstring_helpers.h"
+
+#include <QHBoxLayout>
+#include <QGridLayout>
+
+
+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 (file)
index 0000000..2fed278
--- /dev/null
@@ -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 <QDialog>
+#include <QLabel>
+#include <QLineEdit>
+#include <QDialogButtonBox>
+#include <QPushButton>
+
+
+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
+
index dc569ac0497896b03ebc2a01478859f199016ae9..9338751dc742d61bfe253b619e1729ee47f75602 100644 (file)
@@ -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 <QAction>
 #include <QApplication>
 #include <QToolBar>
 #include <QUrl>
 #include <QScrollBar>
-
-
+#include <QLineEdit>
+#include <QHBoxLayout>
+#include <QDialog>
+#include <QFile>
 
 #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;
+       }
+
+       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 & filename, bool tolastfiles)
+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());
index f1612b99d7e4390c072846bd101801ec26e306f4..07abc9f2f6379d8c826f8e652994fddc8e219d53 100644 (file)
@@ -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);
index ba4a3056e0bd52dfb5fdfafa2376e3c96eecf2f5..cf9227d67f3691abe8c66a743485a79b7f1ad01b 100644 (file)
@@ -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 (file)
index 0000000..111eb35
--- /dev/null
@@ -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 <config.h>
+
+#include "CryptographicEncryption.h"
+
+#include <QDataStream>
+
+
+#ifdef LYX_ENCRYPTION
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#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 (file)
index 0000000..575c834
--- /dev/null
@@ -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 <QCryptographicHash>
+
+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
index e68d1f71906669d7af064e27a97eac1a2521eecb..e8a8ab997be6c6a1c6ca728f61c56427c0d0e0ad 100644 (file)
@@ -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
index 23e6fd0d408a9e6625b7ac7589676ba119951657..8dc58532edfc59c307d049a899defdd5fb56b421 100644 (file)
@@ -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
index 3c67ecdfa7788c69412f36aaf4379993a2909a91..e5476619f37462c9bf9a096da73d77efc5bc98dd 100644 (file)
@@ -38,6 +38,8 @@ liblyxsupport_a_SOURCES = \
        convert.cpp \
        convert.h \
        copied_ptr.h \
+       CryptographicEncryption.h \
+       CryptographicEncryption.cpp \
        debug.cpp \
        debug.h \
        docstream.cpp \