Hello community,

here is the log from the commit of package libfilezilla for openSUSE:Factory 
checked in at 2018-10-29 14:22:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libfilezilla (Old)
 and      /work/SRC/openSUSE:Factory/.libfilezilla.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libfilezilla"

Mon Oct 29 14:22:31 2018 rev:21 rq:645011 version:0.15.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libfilezilla/libfilezilla.changes        
2018-10-15 09:48:06.123059274 +0200
+++ /work/SRC/openSUSE:Factory/.libfilezilla.new/libfilezilla.changes   
2018-10-29 14:58:52.738017542 +0100
@@ -1,0 +2,17 @@
+Sat Oct 27 08:35:12 UTC 2018 - [email protected]
+
+- update to 0.15.0
+  * New features:
+    - libfilezilla now depends on Nettle >= 3.1
+    - Added fz::sha512, fz::sha256, fz::sha1 and fz::md5 hash 
+      functions
+    - Added fz::hash_accumulator
+    - Added fz::hmac_sha256 HMAC function
+    - Added asymmetric encryption scheme using X25519
+    - Added signature scheme using Ed25519
+  * Bugfixes and minor changes:
+    - Changed and documented semantics of the return value of 
+      fz::remove_file, removing a non-existing file is not an error
+- Drop extra COPYING file and use it from upstream
+
+-------------------------------------------------------------------

Old:
----
  COPYING
  libfilezilla-0.14.0.tar.bz2

New:
----
  libfilezilla-0.15.0.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libfilezilla.spec ++++++
--- /var/tmp/diff_new_pack.ZNyW9V/_old  2018-10-29 14:58:53.326018313 +0100
+++ /var/tmp/diff_new_pack.ZNyW9V/_new  2018-10-29 14:58:53.330018318 +0100
@@ -20,14 +20,13 @@
 %define libname                %{name}%{major}
 %define develname      %{name}-devel
 Name:           libfilezilla
-Version:        0.14.0
+Version:        0.15.0
 Release:        0
 Summary:        C++ library for filezilla
 License:        GPL-2.0-or-later
 Group:          Development/Libraries/C and C++
 Url:            https://lib.filezilla-project.org/
 Source0:        
https://download.filezilla-project.org/libfilezilla/%{name}-%{version}.tar.bz2
-Source1:        COPYING
 Patch0:         %{name}-date-time.patch
 BuildRequires:  autoconf
 BuildRequires:  automake
@@ -37,6 +36,7 @@
 BuildRequires:  libtool
 BuildRequires:  pkg-config
 BuildRequires:  pkgconfig(cppunit)
+BuildRequires:  pkgconfig(nettle) >= 3.1
 %if 0%{?suse_version} <= 1500
 # FileZilla requires C++14 support.
 BuildRequires:  gcc7-c++
@@ -77,8 +77,6 @@
 %prep
 %setup -q
 %patch0
-#copy in up to date GPL-2.0 license.
-cp -v %{SOURCE1} .
 
 %build
 %if 0%{?suse_version} <= 1500
@@ -111,7 +109,8 @@
 
 %files -n %{libname}
 %defattr(-,root,root)
-%doc AUTHORS ChangeLog NEWS README COPYING
+%license COPYING
+%doc AUTHORS ChangeLog NEWS README
 %{_libdir}/%{name}.so.%{major}*
 
 %files -n %{develname}

++++++ libfilezilla-0.14.0.tar.bz2 -> libfilezilla-0.15.0.tar.bz2 ++++++
++++ 2167 lines of diff (skipped)
++++    retrying with extended exclude list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/INSTALL new/libfilezilla-0.15.0/INSTALL
--- old/libfilezilla-0.14.0/INSTALL     2017-07-10 19:43:37.000000000 +0200
+++ new/libfilezilla-0.15.0/INSTALL     2018-10-19 12:13:12.000000000 +0200
@@ -8,6 +8,7 @@
 To compile libfilezilla, the following tools need to be installed:
 - A C++14 compiler, e.g. a recent GCC or Clang
 - GNU make
+- Nettle >= 3.1
 
 Optional tools:
 - recent automake, autoconf, libtool if you plan to change configure.in or 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/NEWS new/libfilezilla-0.15.0/NEWS
--- old/libfilezilla-0.14.0/NEWS        2018-10-04 13:17:12.000000000 +0200
+++ new/libfilezilla-0.15.0/NEWS        2018-10-19 12:12:06.000000000 +0200
@@ -1,3 +1,13 @@
+0.15.0 (2018-10-19)
+
++ libfilezilla now depends on Nettle >= 3.1
++ Added fz::sha512, fz::sha256, fz::sha1 and fz::md5 hash functions
++ Added fz::hash_accumulator
++ Added fz::hmac_sha256 HMAC function
++ Added asymmetric encryption scheme using X25519
++ Added signature scheme using Ed25519
+- Changed and documented semantics of the return value of fz::remove_file, 
removing a non-existing file is not an error
+
 0.14.0 (2018-10-04)
 
 + Added fz::equal_insensitive_ascii
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/configure.ac new/libfilezilla-0.15.0/configure.ac
--- old/libfilezilla-0.14.0/configure.ac        2018-10-04 13:17:12.000000000 
+0200
+++ new/libfilezilla-0.15.0/configure.ac        2018-10-19 12:00:57.000000000 
+0200
@@ -1,4 +1,4 @@
-AC_INIT([libfilezilla],[0.14.0],[[email protected]],[],[https://lib.filezilla-project.org/])
+AC_INIT([libfilezilla],[0.15.0],[[email protected]],[],[https://lib.filezilla-project.org/])
 AC_CONFIG_HEADERS([lib/libfilezilla/private/config.hpp])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR(config)
@@ -100,6 +100,23 @@
 fi
 
 
+# Nettle
+# ------
+
+PKG_CHECK_MODULES([NETTLE], [nettle >= 3.1],, [
+  AC_MSG_ERROR([nettle 3.1 greater was not found. You can get it from 
https://www.lysator.liu.se/~nisse/nettle/])
+])
+
+AC_SUBST(NETTLE_LIBS)
+AC_SUBST(NETTLE_CFLAGS)
+
+PKG_CHECK_MODULES([HOGWEED], [hogweed >= 3.1],, [
+  AC_MSG_ERROR([hogweed 3.1 greater was not found. You can get it from 
https://www.lysator.liu.se/~nisse/nettle/])
+])
+
+AC_SUBST(HOGWEED_LIBS)
+AC_SUBST(HOGWEED_CFLAGS)
+
 
 # Check for windres on MinGW builds
 # ---------------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/Makefile.am new/libfilezilla-0.15.0/lib/Makefile.am
--- old/libfilezilla-0.14.0/lib/Makefile.am     2017-09-29 10:41:58.000000000 
+0200
+++ new/libfilezilla-0.15.0/lib/Makefile.am     2018-10-19 12:00:50.000000000 
+0200
@@ -3,15 +3,18 @@
 libfilezilla_la_SOURCES = \
        buffer.cpp \
        encode.cpp \
+       encryption.cpp \
        event.cpp \
        event_handler.cpp \
        event_loop.cpp \
        file.cpp \
+       hash.cpp \
        iputils.cpp \
        local_filesys.cpp \
        mutex.cpp \
        process.cpp \
        recursive_remove.cpp \
+       signature.cpp \
        string.cpp \
        thread.cpp \
        thread_pool.cpp \
@@ -24,11 +27,13 @@
        libfilezilla/apply.hpp \
        libfilezilla/buffer.hpp \
        libfilezilla/encode.hpp \
+       libfilezilla/encryption.hpp \
        libfilezilla/event.hpp \
        libfilezilla/event_handler.hpp \
        libfilezilla/event_loop.hpp \
        libfilezilla/file.hpp \
        libfilezilla/format.hpp \
+       libfilezilla/hash.hpp \
        libfilezilla/iputils.hpp \
        libfilezilla/libfilezilla.hpp \
        libfilezilla/local_filesys.hpp \
@@ -37,6 +42,7 @@
        libfilezilla/process.hpp \
        libfilezilla/recursive_remove.hpp \
        libfilezilla/shared.hpp \
+       libfilezilla/signature.hpp \
        libfilezilla/string.hpp \
        libfilezilla/thread.hpp \
        libfilezilla/thread_pool.hpp \
@@ -51,6 +57,7 @@
 
 libfilezilla_la_CPPFLAGS = $(AM_CPPFLAGS)
 libfilezilla_la_CPPFLAGS += -DBUILDING_LIBFILEZILLA
+libfilezilla_la_CPPFLAGS += $(NETTLE_CFLAGS)
 
 # Needed for version.hpp in out-of-tree builds
 libfilezilla_la_CPPFLAGS += -I$(srcdir)/libfilezilla
@@ -62,6 +69,8 @@
 libfilezilla_la_LDFLAGS += -no-undefined
 libfilezilla_la_LIBADD = $(libdeps)
 
+libfilezilla_la_LIBADD += $(NETTLE_LIBS) $(HOGWEED_LIBS)
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libfilezilla.pc
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/encode.cpp new/libfilezilla-0.15.0/lib/encode.cpp
--- old/libfilezilla-0.14.0/lib/encode.cpp      2018-05-06 11:22:43.000000000 
+0200
+++ new/libfilezilla-0.15.0/lib/encode.cpp      2018-10-19 12:00:50.000000000 
+0200
@@ -1,8 +1,13 @@
 #include "libfilezilla/encode.hpp"
 
 namespace fz {
-std::string base64_encode(std::string const& in, base64_type type, bool pad)
+
+namespace {
+template<typename DataContainer>
+std::string base64_encode_impl(DataContainer const& in, base64_type type, bool 
pad)
 {
+       static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad 
container type");
+
        std::string::value_type const* const base64_chars =
                 (type == base64_type::standard)
                        ? 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
@@ -47,6 +52,17 @@
 
        return ret;
 }
+}
+
+std::string base64_encode(std::string const& in, base64_type type, bool pad)
+{
+       return base64_encode_impl(in, type, pad);
+}
+
+std::string base64_encode(std::vector<uint8_t> const& in, base64_type type, 
bool pad)
+{
+       return base64_encode_impl(in, type, pad);
+}
 
 std::string base64_decode(std::string const& in)
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/encryption.cpp 
new/libfilezilla-0.15.0/lib/encryption.cpp
--- old/libfilezilla-0.14.0/lib/encryption.cpp  1970-01-01 01:00:00.000000000 
+0100
+++ new/libfilezilla-0.15.0/lib/encryption.cpp  2018-10-19 12:00:50.000000000 
+0200
@@ -0,0 +1,271 @@
+#include "libfilezilla/encryption.hpp"
+
+#include "libfilezilla/encode.hpp"
+#include "libfilezilla/hash.hpp"
+#include "libfilezilla/util.hpp"
+
+#include <cstring>
+
+#include <nettle/aes.h>
+#include <nettle/ctr.h>
+#include <nettle/curve25519.h>
+#include <nettle/gcm.h>
+#include <nettle/memops.h>
+#include <nettle/pbkdf2.h>
+#include <nettle/sha2.h>
+
+namespace fz {
+
+std::string public_key::to_base64() const
+{
+       auto raw = std::string(key_.cbegin(), key_.cend());
+       raw += std::string(salt_.cbegin(), salt_.cend());
+       return fz::base64_encode(raw);
+}
+
+public_key public_key::from_base64(std::string const& base64)
+{
+       public_key ret;
+
+       auto raw = fz::base64_decode(base64);
+       if (raw.size() == key_size + salt_size) {
+               auto p = reinterpret_cast<uint8_t const*>(&raw[0]);
+               ret.key_.assign(p, p + key_size);
+               ret.salt_.assign(p + key_size, p + key_size + salt_size);
+       }
+
+       return ret;
+}
+
+private_key private_key::generate()
+{
+       private_key ret;
+
+       ret.key_ = fz::random_bytes(key_size);
+       ret.key_[0] &= 248;
+       ret.key_[31] &= 127;
+       ret.key_[31] |= 64;
+
+       ret.salt_ = fz::random_bytes(salt_size);
+
+       return ret;
+}
+
+public_key private_key::pubkey() const
+{
+       public_key ret;
+
+       if (*this) {
+               static const uint8_t nine[32]{
+                       9, 0, 0, 0, 0, 0, 0, 0,
+                       0, 0, 0, 0, 0, 0, 0, 0,
+                       0, 0, 0, 0, 0, 0, 0, 0,
+                       0, 0, 0, 0, 0, 0, 0, 0 };
+
+               ret.key_.resize(32);
+               nettle_curve25519_mul(&ret.key_[0], &key_[0], nine);
+
+               ret.salt_ = salt_;
+       }
+
+       return ret;
+}
+
+private_key private_key::from_password(std::vector<uint8_t> const& password, 
std::vector<uint8_t> const& salt)
+{
+       private_key ret;
+
+       if (!password.empty() && salt.size() == salt_size) {
+
+               std::vector<uint8_t> key;
+               key.resize(key_size);
+               nettle_pbkdf2_hmac_sha256(password.size(), &password[0], 
100000, salt_size, &salt[0], 32, &key[0]);
+               key[0] &= 248;
+               key[31] &= 127;
+               key[31] |= 64;
+
+               ret.key_ = std::move(key);
+               ret.salt_ = salt;
+       }
+
+       return ret;
+}
+
+std::string private_key::to_base64() const
+{
+       auto raw = std::string(key_.cbegin(), key_.cend());
+       raw += std::string(salt_.cbegin(), salt_.cend());
+       return fz::base64_encode(raw);
+}
+
+private_key private_key::from_base64(std::string const& base64)
+{
+       private_key ret;
+
+       auto raw = fz::base64_decode(base64);
+       if (raw.size() == key_size + salt_size) {
+               auto p = reinterpret_cast<uint8_t const*>(&raw[0]);
+               ret.key_.assign(p, p + key_size);
+               ret.key_[0] &= 248;
+               ret.key_[31] &= 127;
+               ret.key_[31] |= 64;
+               ret.salt_.assign(p + key_size, p + key_size + salt_size);
+       }
+
+       return ret;
+}
+
+
+std::vector<uint8_t> private_key::shared_secret(public_key const& pub) const
+{
+       std::vector<uint8_t> ret;
+
+       if (*this && pub) {
+               ret.resize(32);
+
+               nettle_curve25519_mul(&ret[0], &key_[0], &pub.key_[0]);
+       }
+
+       return ret;
+}
+
+std::vector<uint8_t> encrypt(uint8_t const* plain, size_t size, public_key 
const& pub, bool authenticated)
+{
+       std::vector<uint8_t> ret;
+
+       private_key ephemeral = private_key::generate();
+       public_key ephemeral_pub = ephemeral.pubkey();
+
+       if (pub && ephemeral && ephemeral_pub) {
+               // Generate shared secret from pub and ephemeral
+               std::vector<uint8_t> secret = ephemeral.shared_secret(pub);
+
+               // Derive AES2556 key and CTR nonce from shared secret
+               std::vector<uint8_t> const aes_key = 
hash_accumulator(hash_algorithm::sha256) << ephemeral_pub.salt_ << 0 << secret 
<< ephemeral_pub.key_ << pub.key_ << pub.salt_;
+
+               if (authenticated) {
+                       std::vector<uint8_t> iv = 
hash_accumulator(hash_algorithm::sha256) << ephemeral_pub.salt_ << 2 << secret 
<< ephemeral_pub.key_ << pub.key_ << pub.salt_;
+                       static_assert(SHA256_DIGEST_SIZE >= GCM_IV_SIZE, "iv 
too small");
+                       iv.resize(GCM_IV_SIZE);
+
+                       gcm_aes256_ctx ctx;
+                       nettle_gcm_aes256_set_key(&ctx, &aes_key[0]);
+                       nettle_gcm_aes256_set_iv(&ctx, GCM_IV_SIZE, &iv[0]);
+
+                       // Encrypt plaintext with AES256-GCM
+                       ret.resize(public_key::key_size + public_key::salt_size 
+ size + GCM_DIGEST_SIZE);
+                       if (size) {
+                               nettle_gcm_aes256_encrypt(&ctx, size, 
&ret[public_key::key_size + public_key::salt_size], plain);
+                       }
+
+                       // Return 
ephemeral_pub.key_||ephemeral_pub.salt_||ciphertext||tag
+                       memcpy(&ret[0], &ephemeral_pub.key_[0], 
public_key::key_size);
+                       memcpy(&ret[public_key::key_size], 
&ephemeral_pub.salt_[0], public_key::salt_size);
+                       nettle_gcm_aes256_digest(&ctx, GCM_DIGEST_SIZE, 
&ret[public_key::key_size + public_key::salt_size + size]);
+               }
+               else {
+                       std::vector<uint8_t> ctr = 
hash_accumulator(hash_algorithm::sha256) << ephemeral_pub.salt_ << 1 << secret 
<< ephemeral_pub.key_ << pub.key_ << pub.salt_;
+
+                       aes256_ctx ctx;
+                       nettle_aes256_set_encrypt_key(&ctx, &aes_key[0]);
+
+                       // Encrypt plaintext with AES256-CTR
+                       ret.resize(public_key::key_size + public_key::salt_size 
+ size);
+                       if (size) {
+                               nettle_ctr_crypt(&ctx, 
reinterpret_cast<nettle_cipher_func*>(nettle_aes256_encrypt), 16, &ctr[0], 
size, &ret[public_key::key_size + public_key::salt_size], plain);
+                       }
+
+                       // Return 
ephemeral_pub.key_||ephemeral_pub.salt_||ciphertext
+                       memcpy(&ret[0], &ephemeral_pub.key_[0], 
public_key::key_size);
+                       memcpy(&ret[public_key::key_size], 
&ephemeral_pub.salt_[0], public_key::salt_size);
+               }
+       }
+
+       return ret;
+}
+
+std::vector<uint8_t> encrypt(std::vector<uint8_t> const& plain, public_key 
const& pub, bool authenticated)
+{
+       return encrypt(plain.data(), plain.size(), pub, authenticated);
+}
+
+std::vector<uint8_t> encrypt(std::string const& plain, public_key const& pub, 
bool authenticated)
+{
+       return encrypt(reinterpret_cast<uint8_t const*>(plain.c_str()), 
plain.size(), pub, authenticated);
+}
+
+std::vector<uint8_t> decrypt(uint8_t const* cipher, size_t size, private_key 
const& priv, bool authenticated)
+{
+       size_t const overhead = public_key::key_size + public_key::salt_size + 
(authenticated ? GCM_DIGEST_SIZE : 0);
+
+       std::vector<uint8_t> ret;
+
+       if (priv && size >= overhead && cipher) {
+               size_t const message_size = size - overhead;
+
+               // Extract ephemeral_pub from cipher
+               public_key ephemeral_pub;
+               ephemeral_pub.key_.resize(public_key::key_size);
+               ephemeral_pub.salt_.resize(public_key::salt_size);
+               memcpy(&ephemeral_pub.key_[0], cipher, public_key::key_size);
+               memcpy(&ephemeral_pub.salt_[0], cipher + public_key::key_size, 
public_key::salt_size);
+
+               // Generate shared secret from ephemeral_pub and priv
+               std::vector<uint8_t> const secret = 
priv.shared_secret(ephemeral_pub);
+
+               public_key const pub = priv.pubkey();
+               std::vector<uint8_t> const aes_key = 
hash_accumulator(hash_algorithm::sha256) << ephemeral_pub.salt_ << 0 << secret 
<< ephemeral_pub.key_ << pub.key_ << pub.salt_;
+
+               if (authenticated) {
+                       // Derive AES2556 key and GCM IV from shared secret
+                       std::vector<uint8_t> iv = 
hash_accumulator(hash_algorithm::sha256) << ephemeral_pub.salt_ << 2 << secret 
<< ephemeral_pub.key_ << pub.key_ << pub.salt_;
+                       static_assert(SHA256_DIGEST_SIZE >= GCM_IV_SIZE, "iv 
too small");
+                       iv.resize(GCM_IV_SIZE);
+
+                       gcm_aes256_ctx ctx;
+                       nettle_gcm_aes256_set_key(&ctx, &aes_key[0]);
+                       nettle_gcm_aes256_set_iv(&ctx, GCM_IV_SIZE, &iv[0]);
+
+                       // Decrypt ciphertext with AES256-GCM
+                       ret.resize(message_size);
+                       if (message_size) {
+                               nettle_gcm_aes256_decrypt(&ctx, message_size, 
&ret[0], cipher +public_key::key_size + public_key::salt_size);
+                       }
+
+                       // Last but not least, verify the tag
+                       uint8_t tag[GCM_DIGEST_SIZE];
+                       nettle_gcm_aes256_digest(&ctx, GCM_DIGEST_SIZE, tag);
+                       if (!nettle_memeql_sec(tag, cipher + size - 
GCM_DIGEST_SIZE, GCM_DIGEST_SIZE)) {
+                               ret.clear();
+                       }
+               }
+               else {
+                       // Derive AES2556 key and CTR nonce from shared secret
+                       std::vector<uint8_t> ctr = 
hash_accumulator(hash_algorithm::sha256) << ephemeral_pub.salt_ << 1 << secret 
<< ephemeral_pub.key_ << pub.key_ << pub.salt_;
+
+                       aes256_ctx ctx;
+                       nettle_aes256_set_encrypt_key(&ctx, &aes_key[0]);
+
+                       // Decrypt ciphertext with AES256-CTR
+                       ret.resize(message_size);
+                       if (message_size) {
+                               nettle_ctr_crypt(&ctx, 
reinterpret_cast<nettle_cipher_func*>(nettle_aes256_encrypt), 16, &ctr[0], 
ret.size(), &ret[0], &cipher[public_key::key_size + public_key::salt_size]);
+                       }
+               }
+       }
+
+       // Return the plaintext
+       return ret;
+}
+
+std::vector<uint8_t> decrypt(std::vector<uint8_t> const& cipher, private_key 
const& priv, bool authenticated)
+{
+       return decrypt(cipher.data(), cipher.size(), priv, authenticated);
+}
+
+std::vector<uint8_t> decrypt(std::string const& cipher, private_key const& 
priv, bool authenticated)
+{
+       return decrypt(reinterpret_cast<uint8_t const*>(cipher.c_str()), 
cipher.size(), priv, authenticated);
+}
+
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/file.cpp new/libfilezilla-0.15.0/lib/file.cpp
--- old/libfilezilla-0.14.0/lib/file.cpp        2017-10-30 18:14:16.000000000 
+0100
+++ new/libfilezilla-0.15.0/lib/file.cpp        2018-10-19 12:00:50.000000000 
+0200
@@ -126,7 +126,12 @@
 
 bool remove_file(fz::native_string const& name)
 {
-       return DeleteFileW(name.c_str()) != 0;
+       bool ret = DeleteFileW(name.c_str()) != 0;
+       if (!ret && GetLastError() == ERROR_FILE_NOT_FOUND) {
+               ret = true;
+       }
+
+       return ret;
 }
 
 bool file::fsync()
@@ -242,7 +247,11 @@
 
 bool remove_file(fz::native_string const& name)
 {
-       return unlink(name.c_str()) == 0;
+       bool ret = unlink(name.c_str()) == 0;
+       if (!ret && errno == ENOENT) {
+               ret = true;
+       }
+       return ret;
 }
 
 bool file::fsync()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/hash.cpp new/libfilezilla-0.15.0/lib/hash.cpp
--- old/libfilezilla-0.14.0/lib/hash.cpp        1970-01-01 01:00:00.000000000 
+0100
+++ new/libfilezilla-0.15.0/lib/hash.cpp        2018-10-19 12:00:50.000000000 
+0200
@@ -0,0 +1,311 @@
+#include "libfilezilla/libfilezilla.hpp"
+
+#include "libfilezilla/hash.hpp"
+
+#include <nettle/md5.h>
+#include <nettle/sha2.h>
+#include <nettle/hmac.h>
+
+namespace fz {
+
+class hash_accumulator::impl
+{
+public:
+       virtual ~impl() = default;
+
+       virtual void update(uint8_t const* data, size_t size) = 0;
+       virtual void reinit() = 0;
+       virtual std::vector<uint8_t> digest() = 0;
+};
+
+class hash_accumulator_md5 final : public hash_accumulator::impl
+{
+public:
+       virtual void update(uint8_t const* data, size_t size) override
+       {
+               nettle_md5_update(&ctx_, size, data);
+       }
+
+       virtual void reinit() override
+       {
+               nettle_md5_init(&ctx_);
+       }
+
+       virtual std::vector<uint8_t> digest() override
+       {
+               std::vector<uint8_t> ret;
+               ret.resize(MD5_DIGEST_SIZE);
+               nettle_md5_digest(&ctx_, ret.size(), &ret[0]);
+               return ret;
+       }
+
+private:
+       md5_ctx ctx_;
+};
+
+class hash_accumulator_sha1 final : public hash_accumulator::impl
+{
+public:
+       virtual void update(uint8_t const* data, size_t size) override
+       {
+               nettle_sha1_update(&ctx_, size, data);
+       }
+
+       virtual void reinit() override
+       {
+               nettle_sha1_init(&ctx_);
+       }
+
+       virtual std::vector<uint8_t> digest() override
+       {
+               std::vector<uint8_t> ret;
+               ret.resize(SHA1_DIGEST_SIZE);
+               nettle_sha1_digest(&ctx_, ret.size(), &ret[0]);
+               return ret;
+       }
+
+private:
+       sha1_ctx ctx_;
+};
+
+class hash_accumulator_sha256 final : public hash_accumulator::impl
+{
+public:
+       virtual void update(uint8_t const* data, size_t size) override
+       {
+               nettle_sha256_update(&ctx_, size, data);
+       }
+
+       virtual void reinit() override
+       {
+               nettle_sha256_init(&ctx_);
+       }
+
+       virtual std::vector<uint8_t> digest() override
+       {
+               std::vector<uint8_t> ret;
+               ret.resize(SHA256_DIGEST_SIZE);
+               nettle_sha256_digest(&ctx_, ret.size(), &ret[0]);
+               return ret;
+       }
+
+private:
+       sha256_ctx ctx_;
+};
+
+class hash_accumulator_sha512 final : public hash_accumulator::impl
+{
+public:
+       virtual void update(uint8_t const* data, size_t size) override
+       {
+               nettle_sha512_update(&ctx_, size, data);
+       }
+
+       virtual void reinit() override
+       {
+               nettle_sha512_init(&ctx_);
+       }
+
+       virtual std::vector<uint8_t> digest() override
+       {
+               std::vector<uint8_t> ret;
+               ret.resize(SHA512_DIGEST_SIZE);
+               nettle_sha512_digest(&ctx_, ret.size(), &ret[0]);
+               return ret;
+       }
+
+private:
+       sha512_ctx ctx_;
+};
+
+hash_accumulator::hash_accumulator(hash_algorithm algorithm)
+{
+       switch (algorithm) {
+       case hash_algorithm::md5:
+               impl_ = new hash_accumulator_md5;
+               break;
+       case hash_algorithm::sha1:
+               impl_ = new hash_accumulator_sha1;
+               break;
+       case hash_algorithm::sha256:
+               impl_ = new hash_accumulator_sha256;
+               break;
+       case hash_algorithm::sha512:
+               impl_ = new hash_accumulator_sha512;
+               break;
+       }
+
+       impl_->reinit();
+}
+
+hash_accumulator::~hash_accumulator()
+{
+       delete impl_;
+}
+
+void hash_accumulator::reinit()
+{
+       impl_->reinit();
+}
+
+void hash_accumulator::update(std::string const& data)
+{
+       if (!data.empty()) {
+               impl_->update(reinterpret_cast<uint8_t const*>(&data[0]), 
data.size());
+       }
+}
+
+void hash_accumulator::update(std::vector<uint8_t> const& data)
+{
+       if (!data.empty()) {
+               impl_->update(&data[0], data.size());
+       }
+}
+
+void hash_accumulator::update(uint8_t const* data, size_t size)
+{
+       impl_->update(data, size);
+}
+
+std::vector<uint8_t> hash_accumulator::digest()
+{
+       return impl_->digest();
+}
+
+namespace {
+// In C++17, require ContiguousContainer
+template<typename DataContainer>
+std::vector<uint8_t> md5_impl(DataContainer const& in)
+{
+       static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad 
container type");
+
+       hash_accumulator_md5 acc;
+       acc.reinit();
+       if (!in.empty()) {
+               acc.update(reinterpret_cast<uint8_t const*>(&in[0]), in.size());
+       }
+       return acc.digest();
+}
+
+template<typename DataContainer>
+std::vector<uint8_t> sha1_impl(DataContainer const& in)
+{
+       static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad 
container type");
+
+       hash_accumulator_sha1 acc;
+       acc.reinit();
+       if (!in.empty()) {
+               acc.update(reinterpret_cast<uint8_t const*>(&in[0]), in.size());
+       }
+       return acc.digest();
+}
+
+template<typename DataContainer>
+std::vector<uint8_t> sha256_impl(DataContainer const& in)
+{
+       static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad 
container type");
+
+       hash_accumulator_sha256 acc;
+       acc.reinit();
+       if (!in.empty()) {
+               acc.update(reinterpret_cast<uint8_t const*>(&in[0]), in.size());
+       }
+       return acc.digest();
+}
+
+template<typename DataContainer>
+std::vector<uint8_t> sha512_impl(DataContainer const& in)
+{
+       static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad 
container type");
+
+       hash_accumulator_sha512 acc;
+       acc.reinit();
+       if (!in.empty()) {
+               acc.update(reinterpret_cast<uint8_t const*>(&in[0]), in.size());
+       }
+       return acc.digest();
+}
+
+template<typename KeyContainer, typename DataContainer>
+std::vector<uint8_t> hmac_sha256_impl(KeyContainer const& key, DataContainer 
const& data)
+{
+       static_assert(sizeof(typename KeyContainer::value_type) == 1, "Bad 
container type");
+       static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad 
container type");
+
+       std::vector<uint8_t> ret;
+
+       hmac_sha256_ctx ctx;
+       nettle_hmac_sha256_set_key(&ctx, key.size(), key.empty() ? nullptr : 
reinterpret_cast<uint8_t const*>(&key[0]));
+
+       if (!data.empty()) {
+               nettle_hmac_sha256_update(&ctx, data.size(), 
reinterpret_cast<uint8_t const*>(&data[0]));
+       }
+
+       ret.resize(SHA256_DIGEST_SIZE);
+       nettle_hmac_sha256_digest(&ctx, ret.size(), &ret[0]);
+
+       return ret;
+}
+}
+
+std::vector<uint8_t> md5(std::vector<uint8_t> const& data)
+{
+       return md5_impl(data);
+}
+
+std::vector<uint8_t> md5(std::string const& data)
+{
+       return md5_impl(data);
+}
+
+std::vector<uint8_t> sha1(std::vector<uint8_t> const& data)
+{
+       return sha1_impl(data);
+}
+
+std::vector<uint8_t> sha1(std::string const& data)
+{
+       return sha1_impl(data);
+}
+
+std::vector<uint8_t> sha256(std::vector<uint8_t> const& data)
+{
+       return sha256_impl(data);
+}
+
+std::vector<uint8_t> sha256(std::string const& data)
+{
+       return sha256_impl(data);
+}
+
+std::vector<uint8_t> sha512(std::vector<uint8_t> const& data)
+{
+       return sha512_impl(data);
+}
+
+std::vector<uint8_t> sha512(std::string const& data)
+{
+       return sha512_impl(data);
+}
+
+std::vector<uint8_t> hmac_sha256(std::string const& key, std::string const& 
data)
+{
+       return hmac_sha256_impl(key, data);
+}
+
+std::vector<uint8_t> hmac_sha256(std::vector<uint8_t> const& key, 
std::vector<uint8_t> const& data)
+{
+       return hmac_sha256_impl(key, data);
+}
+
+std::vector<uint8_t> hmac_sha256(std::vector<uint8_t> const& key, std::string 
const& data)
+{
+       return hmac_sha256_impl(key, data);
+}
+
+std::vector<uint8_t> hmac_sha256(std::string const& key, std::vector<uint8_t> 
const& data)
+{
+       return hmac_sha256_impl(key, data);
+}
+
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla/encode.hpp 
new/libfilezilla-0.15.0/lib/libfilezilla/encode.hpp
--- old/libfilezilla-0.14.0/lib/libfilezilla/encode.hpp 2018-05-06 
11:22:43.000000000 +0200
+++ new/libfilezilla-0.15.0/lib/libfilezilla/encode.hpp 2018-10-19 
12:00:50.000000000 +0200
@@ -98,6 +98,7 @@
 
 /// \brief Encodes raw input string to base64
 std::string FZ_PUBLIC_SYMBOL base64_encode(std::string const& in, base64_type 
type = base64_type::standard, bool pad = true);
+std::string FZ_PUBLIC_SYMBOL base64_encode(std::vector<uint8_t> const& in, 
base64_type type = base64_type::standard, bool pad = true);
 
 /**
  * \brief Decodes base64, ignores whitespace. Returns empty string on invalid 
input.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla/encryption.hpp 
new/libfilezilla-0.15.0/lib/libfilezilla/encryption.hpp
--- old/libfilezilla-0.14.0/lib/libfilezilla/encryption.hpp     1970-01-01 
01:00:00.000000000 +0100
+++ new/libfilezilla-0.15.0/lib/libfilezilla/encryption.hpp     2018-10-19 
12:14:34.000000000 +0200
@@ -0,0 +1,153 @@
+#ifndef LIBFILEZILLA_ENCRYPTION_HEADER
+#define LIBFILEZILLA_ENCRYPTION_HEADER
+
+/** \file
+ * \brief Asymmetric encryption scheme using X25519
+ *
+ * See RFC 7748 for the X22519 specs.
+ */
+
+#include "libfilezilla.hpp"
+
+#include <vector>
+#include <string>
+
+namespace fz {
+
+/** \brief Represents a X25519 public key with associated salt
+ *
+ * \sa private_key
+ */
+class FZ_PUBLIC_SYMBOL public_key
+{
+public:
+       /// Size in octets of key and salt.
+       enum {
+               key_size = 32,
+               salt_size = 32
+       };
+
+       explicit operator bool() const {
+               return key_.size() == key_size && salt_.size() == salt_size;
+       }
+
+       bool operator==(public_key const& rhs) const {
+               return key_ == rhs.key_ && salt_ == rhs.salt_;
+       }
+
+       bool operator!=(public_key const& rhs) const {
+               return !(*this == rhs);
+       }
+
+       bool operator<(public_key const& rhs) const {
+               return key_ < rhs.key_ || (key_ == rhs.key_ && salt_ < 
rhs.salt_);
+       }
+
+       std::string to_base64() const;
+       static public_key from_base64(std::string const& base64);
+
+       std::vector<uint8_t> key_;
+       std::vector<uint8_t> salt_;
+};
+
+/** \brief Represents a X25519 private key with associated salt
+ *
+ * \sa public_key
+ */
+class FZ_PUBLIC_SYMBOL private_key
+{
+public:
+       /// Size in octets of key an salt.
+       enum {
+               key_size = 32,
+               salt_size = 32
+       };
+
+       /// Generates a random private key
+       static private_key generate();
+
+       /// Derives a private key using PKBDF2-SHA256 from the given password 
and salt
+       static private_key from_password(std::vector<uint8_t> const& password, 
std::vector<uint8_t> const& salt);
+       static private_key from_password(std::string const& password, 
std::vector<uint8_t> const& salt)
+       {
+               return from_password(std::vector<uint8_t>(password.begin(), 
password.end()), salt);
+       }
+
+       explicit operator bool() const {
+               return key_.size() == key_size && salt_.size() == salt_size;
+       }
+
+       std::vector<uint8_t> const& salt() const {
+               return salt_;
+       }
+
+       /// Calculates the public key corresponding to the private key
+       public_key pubkey() const;
+
+       /// Calculates a shared secret using Elliptic Curve Diffie-Hellman on 
Curve25519 (X25519)
+       std::vector<uint8_t> shared_secret(public_key const& pub) const;
+
+       std::string to_base64() const;
+       static private_key from_base64(std::string const& base64);
+
+private:
+       std::vector<uint8_t> key_;
+       std::vector<uint8_t> salt_;
+};
+
+/** \brief Encrypt the plaintext to the given public key.
+ *
+ * \param authenticated if true, authenticated encryption is used.
+ *
+ * \par Encryption algorithm:
+ *
+ * Let \e M_pub be the key portion, S_e be the salt portion of the pub 
parameter and \e P be the plaintext.
+ *
+ * - First an ephemeral private key \e E_priv with corresponding public key \e 
E_pub and \e S_e is randomly generated
+ * - Using ECDH on Curve25519 (X25519), a shared secret \e R is derived:\n
+ *     <tt>R := X25519(E_priv, M_pub)</tt>
+ * - From \e R, a symmetric AES256 key \e K and a nonce \e IV are derived:
+ *   * <tt>K := SHA256(S_e || 0 || S || E_pub || M_pub || S_m)</tt>
+ *   * <tt>IV := SHA256(S_e || 2 || S || E_pub || M_pub || S_m)</tt> if 
authenticated,\n
+ *     <tt>IV := SHA256(S_e || 1 || S || E_pub || M_pub || S_m)</tt> otherwise
+ * - The plaintext is encrypted into the ciphertext \e C' and authentication 
tag \e T using\n
+ *   <tt>C', T := AES256-GCM(K, IV, P)</tt> if authenticated,\n
+ *   <tt>C' := AES256-CTR(K, IV, P)</tt> T:='' otherwise
+ * - The ciphertext \e C is returned, containing \e E_pub, \e S_e and \e T: \n
+ *     <tt>C := E_pub || S_e || C' || T</tt>
+ */
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL encrypt(std::vector<uint8_t> const& 
plain, public_key const& pub, bool authenticated = true);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL encrypt(std::string const& plain, 
public_key const& pub, bool authenticated = true);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL encrypt(uint8_t const* plain, size_t 
size, public_key const& pub, bool authenticated = true);
+
+/** \brief Decrypt the ciphertext using the given private key.
+ *
+ * \param priv The private matching the public key that was originally used to 
encrypt the data
+ * \param authenticated if true, authenticated encryption is used.
+ *
+ * \returns plaintext on success, empty container on failure
+ *
+ * \par Decryption algorithm:
+ *
+ * Let \e M_priv be the key portion and \e S_m be the salt portion of the priv 
parameter and \e C the ciphertext.
+ *
+ * - First \e C is split into \e E_pub, \e S_e, \e C' and \e T such that\n
+ *   <tt>C: = E_pub || S_e || C1 || T</tt>
+ * - \e M_pub is calculated from \e M_priv
+ * - Using ECDH on Curve25519 (X25519), the shared secret \e R is recovered:\n
+ *     <tt>R := X25519(M_priv, E_pub)</tt>
+ * - From \e R, a symmetric AES256 key \e K and a nonce \e IV are derived:
+ *   * <tt>K := SHA256(S_e || 0 || S || E_pub || M_pub || S_m)</tt>
+ *   * <tt>IV := SHA256(S_e || 2 || S || E_pub || M_pub || S_m)</tt> if 
authenticated,\n
+ *     <tt>IV := SHA256(S_e || 1 || S || E_pub || M_pub || S_m)</tt> otherwise
+ * - The ciphertext is decrypted into the plaintext \e P using\n
+ *   <tt>P, T' := AES256-GCM(K, IV, C')</tt> if authenticated,\n
+ *   <tt>P := AES256-CTR(K, IV, C'), T:=''</tt> otherwise
+ * - If the calculated \e T' matches \e T, then \e P is returned, otherwise 
decryption has failed and nothing is returned.
+ */
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL decrypt(std::vector<uint8_t> const& 
chiper, private_key const& priv, bool authenticated = true);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL decrypt(std::string const& chiper, 
private_key const& priv, bool authenticated = true);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL decrypt(uint8_t const* cipher, size_t 
size, private_key const& priv, bool authenticated = true);
+
+}
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla/file.hpp 
new/libfilezilla-0.15.0/lib/libfilezilla/file.hpp
--- old/libfilezilla-0.14.0/lib/libfilezilla/file.hpp   2017-10-30 
18:14:16.000000000 +0100
+++ new/libfilezilla-0.15.0/lib/libfilezilla/file.hpp   2018-10-19 
12:00:50.000000000 +0200
@@ -142,6 +142,10 @@
 #endif
 };
 
+/** \brief remove the specified file.
+ *
+ * \return true iff the file has been removed or did not exist to begin with.
+ */
 bool FZ_PUBLIC_SYMBOL remove_file(native_string const& name);
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla/hash.hpp 
new/libfilezilla-0.15.0/lib/libfilezilla/hash.hpp
--- old/libfilezilla-0.14.0/lib/libfilezilla/hash.hpp   1970-01-01 
01:00:00.000000000 +0100
+++ new/libfilezilla-0.15.0/lib/libfilezilla/hash.hpp   2018-10-19 
12:00:50.000000000 +0200
@@ -0,0 +1,81 @@
+#ifndef LIBFILEZILLA_HASH_HEADER
+#define LIBFILEZILLA_HASH_HEADER
+
+/** \file
+ * \brief Collection of cryptographic hash and MAC functions
+ */
+
+#include "libfilezilla.hpp"
+
+#include <vector>
+#include <string>
+
+namespace fz {
+
+/// List of supported hashing algorithms
+enum class hash_algorithm
+{
+       md5,
+       sha1,
+       sha256,
+       sha512
+};
+
+/// Accumulator for hashing large amounts of data
+class FZ_PUBLIC_SYMBOL hash_accumulator final
+{
+public:
+       /// Creates an initialized accumulator for the passed algorithm
+       hash_accumulator(hash_algorithm algorithm);
+       ~hash_accumulator();
+
+       hash_accumulator(hash_accumulator const&) = delete;
+       hash_accumulator& operator=(hash_accumulator const&) = delete;
+
+       void reinit();
+
+       void update(std::string const& data);
+       void update(std::vector<uint8_t> const& data);
+       void update(uint8_t const* data, size_t size);
+       void update(uint8_t in) {
+               update(&in, 1);
+       }
+
+       /// Returns the raw digest and reinitalizes the accumulator
+       std::vector<uint8_t> digest();
+
+       operator std::vector<uint8_t>() {
+               return digest();
+       }
+
+       template<typename T>
+       hash_accumulator& operator<<(T && in) {
+               update(std::forward<T>(in));
+               return *this;
+       }
+
+       class impl;
+private:
+       impl* impl_;
+};
+
+/** \brief Standard MD5
+ *
+ * Insecure, avoid using this
+ */
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL md5(std::string const& data);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL md5(std::vector<uint8_t> const& data);
+
+/// \brief Standard SHA256
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL sha256(std::string const& data);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL sha256(std::vector<uint8_t> const& data);
+
+/// \brief Standard HMAC using SHA256
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL hmac_sha256(std::string const& key, 
std::string const& data);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL hmac_sha256(std::vector<uint8_t> const& 
key, std::vector<uint8_t> const& data);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL hmac_sha256(std::vector<uint8_t> const& 
key, std::string const& data);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL hmac_sha256(std::string const& key, 
std::vector<uint8_t> const& data);
+
+}
+
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla/signature.hpp 
new/libfilezilla-0.15.0/lib/libfilezilla/signature.hpp
--- old/libfilezilla-0.14.0/lib/libfilezilla/signature.hpp      1970-01-01 
01:00:00.000000000 +0100
+++ new/libfilezilla-0.15.0/lib/libfilezilla/signature.hpp      2018-10-19 
12:00:50.000000000 +0200
@@ -0,0 +1,98 @@
+#ifndef LIBFILEZILLA_SIGNATURE_HEADER
+#define LIBFILEZILLA_SIGNATURE_HEADER
+
+/** \file
+ * \brief Signature scheme using Ed25519
+ *
+ * See RFC 8032 for the X22519 specs.
+ */
+
+#include "libfilezilla.hpp"
+
+#include <vector>
+#include <string>
+
+namespace fz {
+
+/** \brief Represents a public key to verify messages signed using Ed25519.
+ *
+ * \sa private_signing_key
+ */
+class FZ_PUBLIC_SYMBOL public_verification_key
+{
+public:
+       enum {
+               key_size = 32
+       };
+
+       explicit operator bool() const {
+               return key_.size() == key_size;
+       }
+
+       bool operator==(public_verification_key const& rhs) const {
+               return key_ == rhs.key_;
+       }
+
+       bool operator!=(public_verification_key const& rhs) const {
+               return !(*this == rhs);
+       }
+
+       bool operator<(public_verification_key const& rhs) const {
+               return key_ < rhs.key_;
+       }
+
+       std::string to_base64() const;
+       static public_verification_key from_base64(std::string const& base64);
+
+       std::vector<uint8_t> key_;
+};
+
+/** \brief Represents a private key to sign message with using Ed25519.
+ *
+ * \sa public_verification_key
+ */
+class FZ_PUBLIC_SYMBOL private_signing_key
+{
+public:
+       enum {
+               key_size = 32
+       };
+
+       /// Generates a random private key
+       static private_signing_key generate();
+
+       explicit operator bool() const {
+               return key_.size() == key_size;
+       }
+
+       /// Gets the public key corresponding to the private key
+       public_verification_key pubkey() const;
+
+       std::vector<uint8_t> const& data() const {
+               return key_;
+       }
+
+       std::string to_base64() const; // Keep secret!
+       static private_signing_key from_base64(std::string const& base64);
+
+private:
+       std::vector<uint8_t> key_;
+};
+
+enum {
+       signature_size = 64
+};
+
+/// Returns the message with the signature appended, created using the passed 
private key
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL sign(std::vector<uint8_t> const& 
message, private_signing_key const& priv);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL sign(std::string const& message, 
private_signing_key const& priv);
+std::vector<uint8_t> FZ_PUBLIC_SYMBOL sign(uint8_t const& message, size_t 
const size, private_signing_key const& priv);
+
+/// Verify the message. Returns true iff it has been signed by the private key 
corresponding to the passed public key
+bool FZ_PUBLIC_SYMBOL verify(std::vector<uint8_t> const& message, 
public_verification_key const& pub);
+bool FZ_PUBLIC_SYMBOL verify(std::string const& message, 
public_verification_key const& pub);
+bool FZ_PUBLIC_SYMBOL verify(uint8_t const* message, size_t const size, 
public_verification_key const& pub);
+
+}
+
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla/version.hpp 
new/libfilezilla-0.15.0/lib/libfilezilla/version.hpp
--- old/libfilezilla-0.14.0/lib/libfilezilla/version.hpp        2018-10-04 
13:17:21.000000000 +0200
+++ new/libfilezilla-0.15.0/lib/libfilezilla/version.hpp        2018-10-19 
12:14:45.000000000 +0200
@@ -9,15 +9,15 @@
 
 #include <tuple>
 /// \brief Version string of the libfilezilla headers
-#define LIBFILEZILLA_VERSION "0.14.0"
+#define LIBFILEZILLA_VERSION "0.15.0"
 
 #define LIBFILEZILLA_VERSION_MAJOR  0
-#define LIBFILEZILLA_VERSION_MINOR  14
+#define LIBFILEZILLA_VERSION_MINOR  15
 #define LIBFILEZILLA_VERSION_MICRO  0
 #define LIBFILEZILLA_VERSION_NANO   0
 
 /// \brief Suffix string, e.g. "rc1"
-#define LIBFILEZILLA_VERSION_SUFFIX "0.14.0"
+#define LIBFILEZILLA_VERSION_SUFFIX "0.15.0"
 
 namespace fz {
 /// \brief Get version string of libfilezilla
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla.pc.in 
new/libfilezilla-0.15.0/lib/libfilezilla.pc.in
--- old/libfilezilla-0.14.0/lib/libfilezilla.pc.in      2015-11-02 
14:47:31.000000000 +0100
+++ new/libfilezilla-0.15.0/lib/libfilezilla.pc.in      2018-10-19 
12:00:50.000000000 +0200
@@ -9,3 +9,4 @@
 Version: @PACKAGE_VERSION@
 CFlags: -I${includedir}
 Libs: -L${libdir} -lfilezilla @libdeps@
+Requires.private: nettle >= 3.1, hogweed >= 3.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/libfilezilla.vcxproj 
new/libfilezilla-0.15.0/lib/libfilezilla.vcxproj
--- old/libfilezilla-0.14.0/lib/libfilezilla.vcxproj    2017-09-29 
10:41:58.000000000 +0200
+++ new/libfilezilla-0.15.0/lib/libfilezilla.vcxproj    2018-10-19 
12:00:50.000000000 +0200
@@ -66,6 +66,9 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
   </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="..\Dependencies.props" />
+  </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup>
     <OutDir>$(ProjectDir)$(Platform)_$(Configuration)\</OutDir>
@@ -81,7 +84,7 @@
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
       <BufferSecurityCheck>true</BufferSecurityCheck>
-      <WarningLevel>Level3</WarningLevel>
+      <WarningLevel>Level4</WarningLevel>
       <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
     </ClCompile>
@@ -137,7 +140,6 @@
       <OmitFramePointers>true</OmitFramePointers>
       
<PreprocessorDefinitions>BUILDING_LIBFILEZILLA;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -188,10 +190,12 @@
   <ItemGroup>
     <ClCompile Include="buffer.cpp" />
     <ClCompile Include="encode.cpp" />
+    <ClCompile Include="encryption.cpp" />
     <ClCompile Include="event.cpp" />
     <ClCompile Include="event_handler.cpp" />
     <ClCompile Include="event_loop.cpp" />
     <ClCompile Include="file.cpp" />
+    <ClCompile Include="hash.cpp" />
     <ClCompile Include="iputils.cpp" />
     <ClCompile Include="local_filesys.cpp" />
     <ClCompile Include="mutex.cpp" />
@@ -209,11 +213,13 @@
     <ClInclude Include="libfilezilla\apply.hpp" />
     <ClInclude Include="libfilezilla\buffer.hpp" />
     <ClInclude Include="libfilezilla\encode.hpp" />
+    <ClInclude Include="libfilezilla\encryption.hpp" />
     <ClInclude Include="libfilezilla\event.hpp" />
     <ClInclude Include="libfilezilla\event_handler.hpp" />
     <ClInclude Include="libfilezilla\event_loop.hpp" />
     <ClInclude Include="libfilezilla\file.hpp" />
     <ClInclude Include="libfilezilla\format.hpp" />
+    <ClInclude Include="libfilezilla\hash.hpp" />
     <ClInclude Include="libfilezilla\iputils.hpp" />
     <ClInclude Include="libfilezilla\libfilezilla.hpp" />
     <ClInclude Include="libfilezilla\local_filesys.hpp" />
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/lib/signature.cpp 
new/libfilezilla-0.15.0/lib/signature.cpp
--- old/libfilezilla-0.14.0/lib/signature.cpp   1970-01-01 01:00:00.000000000 
+0100
+++ new/libfilezilla-0.15.0/lib/signature.cpp   2018-10-19 12:00:50.000000000 
+0200
@@ -0,0 +1,115 @@
+#include "libfilezilla/signature.hpp"
+
+#include "libfilezilla/encode.hpp"
+#include "libfilezilla/util.hpp"
+
+#include <nettle/eddsa.h>
+
+namespace fz {
+
+std::string public_verification_key::to_base64() const
+{
+       auto raw = std::string(key_.cbegin(), key_.cend());
+       return fz::base64_encode(raw);
+}
+
+public_verification_key public_verification_key::from_base64(std::string 
const& base64)
+{
+       public_verification_key ret;
+
+       auto raw = fz::base64_decode(base64);
+       if (raw.size() == key_size) {
+               auto p = reinterpret_cast<uint8_t const*>(&raw[0]);
+               ret.key_.assign(p, p + key_size);
+       }
+
+       return ret;
+}
+
+private_signing_key private_signing_key::generate()
+{
+       private_signing_key ret;
+
+       ret.key_ = fz::random_bytes(key_size);
+       return ret;
+}
+
+std::string private_signing_key::to_base64() const
+{
+       auto raw = std::string(key_.cbegin(), key_.cend());
+       return fz::base64_encode(raw);
+}
+
+private_signing_key private_signing_key::from_base64(std::string const& base64)
+{
+       private_signing_key ret;
+
+       auto raw = fz::base64_decode(base64);
+       if (raw.size() == key_size) {
+               auto p = reinterpret_cast<uint8_t const*>(&raw[0]);
+               ret.key_.assign(p, p + key_size);
+       }
+
+       return ret;
+}
+
+public_verification_key private_signing_key::pubkey() const
+{
+       public_verification_key ret;
+
+       if (*this) {
+               ret.key_.resize(public_verification_key::key_size);
+               nettle_ed25519_sha512_public_key(ret.key_.data(), key_.data());
+       }
+
+       return ret;
+}
+
+
+std::vector<uint8_t> sign(uint8_t const* message, size_t const size, 
private_signing_key const& priv)
+{
+       std::vector<uint8_t> ret;
+
+       auto const pub = priv.pubkey();
+       if (priv && pub && size) {
+               ret.reserve(size + signature_size);
+               ret.assign(message, message + size);
+               ret.resize(size + signature_size);
+
+               nettle_ed25519_sha512_sign(pub.key_.data(), priv.data().data(), 
size, ret.data(), ret.data() + size);
+       }
+
+       return ret;
+}
+
+std::vector<uint8_t> sign(std::vector<uint8_t> const& message, 
private_signing_key const& priv)
+{
+       return sign(message.data(), message.size(), priv);
+}
+
+std::vector<uint8_t> sign(std::string const& message, private_signing_key 
const& priv)
+{
+       return sign(reinterpret_cast<uint8_t const*>(message.c_str()), 
message.size(), priv);
+}
+
+
+// Verify the message. Returns true iff it has been signed by the private key 
corresponding to the passed public key
+bool verify(uint8_t const* message, size_t const size, public_verification_key 
const& pub)
+{
+       if (!message || size < signature_size) {
+               return false;
+       }
+       return nettle_ed25519_sha512_verify(pub.key_.data(), size - 
signature_size, message, message + size - signature_size) == 1;
+}
+
+bool verify(std::vector<uint8_t> const& message, public_verification_key 
const& pub)
+{
+       return verify(message.data(), message.size(), pub);
+}
+
+bool verify(std::string const& message, public_verification_key const& pub)
+{
+       return verify(reinterpret_cast<uint8_t const*>(message.c_str()), 
message.size(), pub);
+}
+
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/tests/Makefile.am 
new/libfilezilla-0.15.0/tests/Makefile.am
--- old/libfilezilla-0.14.0/tests/Makefile.am   2018-02-23 09:29:01.000000000 
+0100
+++ new/libfilezilla-0.15.0/tests/Makefile.am   2018-10-19 12:00:50.000000000 
+0200
@@ -5,6 +5,7 @@
 
 test_SOURCES =  test.cpp \
                buffer.cpp \
+               crypto.cpp \
                dispatch.cpp \
                eventloop.cpp \
                format.cpp \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/tests/crypto.cpp 
new/libfilezilla-0.15.0/tests/crypto.cpp
--- old/libfilezilla-0.14.0/tests/crypto.cpp    1970-01-01 01:00:00.000000000 
+0100
+++ new/libfilezilla-0.15.0/tests/crypto.cpp    2018-10-19 12:00:50.000000000 
+0200
@@ -0,0 +1,63 @@
+#include "libfilezilla/encryption.hpp"
+#include "libfilezilla/util.hpp"
+
+#include "test_utils.hpp"
+
+#include <string.h>
+
+class crypto_test final : public CppUnit::TestFixture
+{
+       CPPUNIT_TEST_SUITE(crypto_test);
+       CPPUNIT_TEST(test_encryption);
+       CPPUNIT_TEST(test_encryption_with_password);
+       CPPUNIT_TEST_SUITE_END();
+
+public:
+       void setUp() {}
+       void tearDown() {}
+
+       void test_encryption();
+       void test_encryption_with_password();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(crypto_test);
+
+void crypto_test::test_encryption()
+{
+       auto priv = fz::private_key::generate();
+       priv.generate();
+
+       auto const pub = priv.pubkey();
+
+       std::string const plain = "Hello world";
+
+       auto cipher = fz::encrypt(plain, pub);
+       CPPUNIT_ASSERT(fz::decrypt(cipher, priv) == 
std::vector<uint8_t>(plain.cbegin(), plain.cend()));
+}
+
+
+void crypto_test::test_encryption_with_password()
+{
+       auto const salt = fz::random_bytes(fz::private_key::salt_size);
+
+       std::string const plain = "Hello world";
+       std::vector<uint8_t> cipher;
+
+       {
+               auto priv = fz::private_key::from_password("super secret", 
salt);
+               CPPUNIT_ASSERT(priv);
+
+               auto const pub = priv.pubkey();
+
+               cipher = fz::encrypt(plain, pub);
+       }
+
+
+       {
+               auto priv = fz::private_key::from_password("super secret", 
salt);
+               CPPUNIT_ASSERT(priv);
+
+               CPPUNIT_ASSERT(fz::decrypt(cipher, priv) == 
std::vector<uint8_t>(plain.cbegin(), plain.cend()));
+       }
+
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.14.0/tests/string.cpp 
new/libfilezilla-0.15.0/tests/string.cpp
--- old/libfilezilla-0.14.0/tests/string.cpp    2018-10-04 13:17:12.000000000 
+0200
+++ new/libfilezilla-0.15.0/tests/string.cpp    2018-10-19 12:00:50.000000000 
+0200
@@ -99,9 +99,9 @@
        CPPUNIT_ASSERT_EQUAL(std::string("Zm9vbA"), fz::base64_encode("fool", 
fz::base64_type::standard, false));
        CPPUNIT_ASSERT_EQUAL(std::string("Zm9vbHM"), fz::base64_encode("fools", 
fz::base64_type::standard, false));
 
-       CPPUNIT_ASSERT_EQUAL(std::string("AAECA/3+/w=="), fz::base64_encode({0, 
1, 2, 3, '\xfd', '\xfe', '\xff'}));
+       CPPUNIT_ASSERT_EQUAL(std::string("AAECA/3+/w=="), 
fz::base64_encode(std::string({0, 1, 2, 3, '\xfd', '\xfe', '\xff'})));
 
-       CPPUNIT_ASSERT_EQUAL(std::string("AAECA_3-_w=="), fz::base64_encode({0, 
1, 2, 3, '\xfd', '\xfe', '\xff'}, fz::base64_type::url));
+       CPPUNIT_ASSERT_EQUAL(std::string("AAECA_3-_w=="), 
fz::base64_encode(std::string({0, 1, 2, 3, '\xfd', '\xfe', '\xff'}), 
fz::base64_type::url));
 
        // decode
        CPPUNIT_ASSERT_EQUAL(std::string(""),      fz::base64_decode(""));


Reply via email to