From e0b3bd754f02705a9ba878652c298c5156a016fb Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sat, 13 Jun 2015 10:54:50 -0700 Subject: [PATCH 01/19] Remove gnuism from Makefile According to POSIX, $< is only valid with inference rules, not normal target rules. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bcc7516..dfbf9de 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ coprocess.o: coprocess.cpp coprocess-unix.cpp coprocess-win32.cpp build-man: man/man1/git-crypt.1 man/man1/git-crypt.1: man/git-crypt.xml - $(XSLTPROC) $(DOCBOOK_FLAGS) $(DOCBOOK_XSL) $< + $(XSLTPROC) $(DOCBOOK_FLAGS) $(DOCBOOK_XSL) man/git-crypt.xml # # Clean From c63a727177b611be53415a5e65140d823135d2f4 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Wed, 29 Jul 2015 21:51:11 -0700 Subject: [PATCH 02/19] Mark .gpg files in .git-crypt as binary To remove any possibility of Git treating them as text by accident. Closes #55. --- commands.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/commands.cpp b/commands.cpp index 2b86930..06f42da 100644 --- a/commands.cpp +++ b/commands.cpp @@ -1202,6 +1202,7 @@ int add_gpg_user (int argc, const char** argv) state_gitattributes_file << "# Do not edit this file. To specify the files to encrypt, create your own\n"; state_gitattributes_file << "# .gitattributes file in the directory where your files are.\n"; state_gitattributes_file << "* !filter !diff\n"; + state_gitattributes_file << "*.gpg binary\n"; state_gitattributes_file.close(); if (!state_gitattributes_file) { std::clog << "Error: unable to write " << state_gitattributes_path << std::endl; From b47176e6a8b202380cb11fa3d0b2fd7331ead029 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sat, 26 Dec 2015 13:01:35 -0800 Subject: [PATCH 03/19] Don't hard code path to git-crypt in .git/config on Linux There's a tradeoff. When the path is hardcoded, it's guaranteed that git-crypt will be found no matter where you run git or what your $PATH is. On the other hand, hardcoding means that things break if git-crypt changes location, which could easily happen if you copy a repository to a different system (see https://github.com/AGWA/git-crypt/issues/71 for example). In hindsight, I think this was a bad tradeoff. Now, if git-crypt is invoked as a bare filename (no slashes), the bare filename is placed in .git/config under the assumption that it can be found via $PATH (this assumption will be true as long as git-crypt wasn't resolved via a relative path in $PATH). This logic was already being used on non-Linux OSes and it seemed to work fine. --- util-unix.cpp | 44 ++++++++++++-------------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/util-unix.cpp b/util-unix.cpp index 7c7c05b..6bea324 100644 --- a/util-unix.cpp +++ b/util-unix.cpp @@ -125,40 +125,20 @@ void mkdir_parent (const std::string& path) } } -static std::string readlink (const char* pathname) -{ - std::vector buffer(64); - ssize_t len; - - while ((len = ::readlink(pathname, &buffer[0], buffer.size())) == static_cast(buffer.size())) { - // buffer may have been truncated - grow and try again - buffer.resize(buffer.size() * 2); - } - if (len == -1) { - throw System_error("readlink", pathname, errno); - } - - return std::string(buffer.begin(), buffer.begin() + len); -} - std::string our_exe_path () { - try { - return readlink("/proc/self/exe"); - } catch (const System_error&) { - if (argv0[0] == '/') { - // argv[0] starts with / => it's an absolute path - return argv0; - } else if (std::strchr(argv0, '/')) { - // argv[0] contains / => it a relative path that should be resolved - char* resolved_path_p = realpath(argv0, NULL); - std::string resolved_path(resolved_path_p); - free(resolved_path_p); - return resolved_path; - } else { - // argv[0] is just a bare filename => not much we can do - return argv0; - } + if (argv0[0] == '/') { + // argv[0] starts with / => it's an absolute path + return argv0; + } else if (std::strchr(argv0, '/')) { + // argv[0] contains / => it a relative path that should be resolved + char* resolved_path_p = realpath(argv0, NULL); + std::string resolved_path(resolved_path_p); + free(resolved_path_p); + return resolved_path; + } else { + // argv[0] is just a bare filename => not much we can do + return argv0; } } From 788a6a99f4289745e6bd12fae2ad8014af320a4f Mon Sep 17 00:00:00 2001 From: Kevin Menard Date: Sat, 4 Jun 2016 17:26:49 -0400 Subject: [PATCH 04/19] Make the repo state directory location configurable. Modified-by: Andrew Ayer * Rename a local variable to be more accurate. Signed-off-by: Andrew Ayer --- commands.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/commands.cpp b/commands.cpp index 06f42da..eb98d06 100644 --- a/commands.cpp +++ b/commands.cpp @@ -254,6 +254,27 @@ static std::string get_internal_key_path (const char* key_name) return path; } +static std::string get_git_config (const std::string& name) +{ + // git config --get + std::vector command; + command.push_back("git"); + command.push_back("config"); + command.push_back("--get"); + command.push_back(name); + + std::stringstream output; + + if (!successful_exit(exec_command(command, output))) { + throw Error("'git config' missing value for key '" + name +"'"); + } + + std::string value; + std::getline(output, value); + + return value; +} + static std::string get_repo_state_path () { // git rev-parse --show-toplevel @@ -276,7 +297,18 @@ static std::string get_repo_state_path () throw Error("Could not determine Git working tree - is this a non-bare repo?"); } - path += "/.git-crypt"; + // Check if the repo state dir has been explicitly configured. If so, use that in path construction. + if (git_has_config("git-crypt.repoStateDir")) { + std::string repoStateDir = get_git_config("git-crypt.repoStateDir"); + + // The repoStateDir value must always be relative to git work tree to ensure the repoStateDir can be committed + // along with the remainder of the repository. + path += '/' + repoStateDir; + } else { + // There is no explicitly configured repo state dir configured, so use the default. + path += "/.git-crypt"; + } + return path; } From 553c1b03876e174b117bb5250593217418f425a4 Mon Sep 17 00:00:00 2001 From: "Wael M. Nasreddine" Date: Sun, 30 Jul 2017 12:45:41 -0700 Subject: [PATCH 05/19] crypto: fix for compatibility with openssl 1.1 Closes: #128 --- Makefile | 2 +- crypto-openssl.cpp => crypto-openssl-10.cpp | 5 + crypto-openssl-11.cpp | 119 ++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) rename crypto-openssl.cpp => crypto-openssl-10.cpp (97%) create mode 100644 crypto-openssl-11.cpp diff --git a/Makefile b/Makefile index dfbf9de..735d60f 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ OBJFILES = \ coprocess.o \ fhstream.o -OBJFILES += crypto-openssl.o +OBJFILES += crypto-openssl-10.o crypto-openssl-11.o LDFLAGS += -lcrypto XSLTPROC ?= xsltproc diff --git a/crypto-openssl.cpp b/crypto-openssl-10.cpp similarity index 97% rename from crypto-openssl.cpp rename to crypto-openssl-10.cpp index 6483e86..8cb10f7 100644 --- a/crypto-openssl.cpp +++ b/crypto-openssl-10.cpp @@ -28,6 +28,10 @@ * as that of the covered work. */ +#include + +#if !defined(OPENSSL_API_COMPAT) + #include "crypto.hpp" #include "key.hpp" #include "util.hpp" @@ -113,3 +117,4 @@ void random_bytes (unsigned char* buffer, size_t len) } } +#endif diff --git a/crypto-openssl-11.cpp b/crypto-openssl-11.cpp new file mode 100644 index 0000000..ef85eb5 --- /dev/null +++ b/crypto-openssl-11.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2012, 2014 Andrew Ayer + * + * This file is part of git-crypt. + * + * git-crypt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * git-crypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with git-crypt. If not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify the Program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, the licensors of the Program + * grant you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include + +#if defined(OPENSSL_API_COMPAT) + +#include "crypto.hpp" +#include "key.hpp" +#include "util.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +void init_crypto () +{ + ERR_load_crypto_strings(); +} + +struct Aes_ecb_encryptor::Aes_impl { + AES_KEY key; +}; + +Aes_ecb_encryptor::Aes_ecb_encryptor (const unsigned char* raw_key) +: impl(new Aes_impl) +{ + if (AES_set_encrypt_key(raw_key, KEY_LEN * 8, &(impl->key)) != 0) { + throw Crypto_error("Aes_ctr_encryptor::Aes_ctr_encryptor", "AES_set_encrypt_key failed"); + } +} + +Aes_ecb_encryptor::~Aes_ecb_encryptor () +{ + // Note: Explicit destructor necessary because class contains an auto_ptr + // which contains an incomplete type when the auto_ptr is declared. + + explicit_memset(&impl->key, '\0', sizeof(impl->key)); +} + +void Aes_ecb_encryptor::encrypt(const unsigned char* plain, unsigned char* cipher) +{ + AES_encrypt(plain, cipher, &(impl->key)); +} + +struct Hmac_sha1_state::Hmac_impl { + HMAC_CTX *ctx; +}; + +Hmac_sha1_state::Hmac_sha1_state (const unsigned char* key, size_t key_len) +: impl(new Hmac_impl) +{ + + impl->ctx = HMAC_CTX_new(); + HMAC_Init_ex(impl->ctx, key, key_len, EVP_sha1(), NULL); +} + +Hmac_sha1_state::~Hmac_sha1_state () +{ + HMAC_CTX_free(impl->ctx); +} + +void Hmac_sha1_state::add (const unsigned char* buffer, size_t buffer_len) +{ + HMAC_Update(impl->ctx, buffer, buffer_len); +} + +void Hmac_sha1_state::get (unsigned char* digest) +{ + unsigned int len; + HMAC_Final(impl->ctx, digest, &len); +} + + +void random_bytes (unsigned char* buffer, size_t len) +{ + if (RAND_bytes(buffer, len) != 1) { + std::ostringstream message; + while (unsigned long code = ERR_get_error()) { + char error_string[120]; + ERR_error_string_n(code, error_string, sizeof(error_string)); + message << "OpenSSL Error: " << error_string << "; "; + } + throw Crypto_error("random_bytes", message.str()); + } +} + +#endif From 03ef81e54164668f8cc0055f37227f9f30e7c46b Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Mon, 3 Oct 2016 08:18:48 +0300 Subject: [PATCH 06/19] Update link to git-remote-gcrypt project Fixes #96 Signed-off-by: Andrew Ayer --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index fd982ac..951d1ed 100644 --- a/README +++ b/README @@ -98,7 +98,7 @@ all of the files in a repository. Where git-crypt really shines is where most of your repository is public, but you have a few files (perhaps private keys named *.key, or a file with API credentials) which you need to encrypt. For encrypting an entire repository, consider using a -system like git-remote-gcrypt +system like git-remote-gcrypt instead. (Note: no endorsement is made of git-remote-gcrypt's security.) git-crypt does not encrypt file names, commit messages, symlink targets, From a6170413ebd0409c6eb10f2966dffcc3b5c80f2d Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 10 Sep 2017 12:05:13 -0700 Subject: [PATCH 07/19] Sync README changes to README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aeccc8..58a2a15 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ all of the files in a repository. Where git-crypt really shines is where most of your repository is public, but you have a few files (perhaps private keys named *.key, or a file with API credentials) which you need to encrypt. For encrypting an entire repository, consider using a -system like [git-remote-gcrypt](https://github.com/joeyh/git-remote-gcrypt) +system like [git-remote-gcrypt](https://spwhitton.name/tech/code/git-remote-gcrypt/) instead. (Note: no endorsement is made of git-remote-gcrypt's security.) git-crypt does not encrypt file names, commit messages, symlink targets, From 934914c2c4c88bdad1002853559f767c84b8062f Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 10 Sep 2017 12:09:31 -0700 Subject: [PATCH 08/19] Fix typo in README Closes #83 --- README | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 951d1ed..2bdc1c6 100644 --- a/README +++ b/README @@ -79,7 +79,7 @@ backwards-incompatible changes introduced before version 1.0. SECURITY -git-crypt is more secure that other transparent git encryption systems. +git-crypt is more secure than other transparent git encryption systems. git-crypt encrypts files using AES-256 in CTR mode with a synthetic IV derived from the SHA-1 HMAC of the file. This mode of operation is provably semantically secure under deterministic chosen-plaintext attack. diff --git a/README.md b/README.md index 58a2a15..92c69ca 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ there may be backwards-incompatible changes introduced before version Security -------- -git-crypt is more secure that other transparent git encryption systems. +git-crypt is more secure than other transparent git encryption systems. git-crypt encrypts files using AES-256 in CTR mode with a synthetic IV derived from the SHA-1 HMAC of the file. This mode of operation is provably semantically secure under deterministic chosen-plaintext attack. From 101b738a8d94779f6f99de5aaf7123789fb17228 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 10 Sep 2017 12:13:04 -0700 Subject: [PATCH 09/19] Read gpg program from git config gpg.program ; ported from fork by alanrossmachinery Modified-By: Andrew Ayer * Make whitespace conform to project conventions Closes #89 Closes #65 --- commands.cpp | 2 +- commands.hpp | 3 +++ gpg.cpp | 20 +++++++++++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/commands.cpp b/commands.cpp index eb98d06..0972823 100644 --- a/commands.cpp +++ b/commands.cpp @@ -254,7 +254,7 @@ static std::string get_internal_key_path (const char* key_name) return path; } -static std::string get_git_config (const std::string& name) +std::string get_git_config (const std::string& name) { // git config --get std::vector command; diff --git a/commands.hpp b/commands.hpp index 32caa0f..f441e93 100644 --- a/commands.hpp +++ b/commands.hpp @@ -70,4 +70,7 @@ void help_migrate_key (std::ostream&); void help_refresh (std::ostream&); void help_status (std::ostream&); +// other +std::string get_git_config (const std::string& name); + #endif diff --git a/gpg.cpp b/gpg.cpp index 04f3f60..bec5892 100644 --- a/gpg.cpp +++ b/gpg.cpp @@ -30,8 +30,18 @@ #include "gpg.hpp" #include "util.hpp" +#include "commands.hpp" #include +static std::string gpg_get_executable() +{ + std::string gpgbin = "gpg"; + try { + gpgbin = get_git_config("gpg.program"); + } catch (...) { + } + return gpgbin; +} static std::string gpg_nth_column (const std::string& line, unsigned int col) { std::string::size_type pos = 0; @@ -62,7 +72,7 @@ std::string gpg_get_uid (const std::string& fingerprint) { // gpg --batch --with-colons --fixed-list-mode --list-keys 0x7A399B2DB06D039020CD1CE1D0F3702D61489532 std::vector command; - command.push_back("gpg"); + command.push_back(gpg_get_executable()); command.push_back("--batch"); command.push_back("--with-colons"); command.push_back("--fixed-list-mode"); @@ -94,7 +104,7 @@ std::vector gpg_lookup_key (const std::string& query) // gpg --batch --with-colons --fingerprint --list-keys jsmith@example.com std::vector command; - command.push_back("gpg"); + command.push_back(gpg_get_executable()); command.push_back("--batch"); command.push_back("--with-colons"); command.push_back("--fingerprint"); @@ -125,7 +135,7 @@ std::vector gpg_list_secret_keys () { // gpg --batch --with-colons --list-secret-keys --fingerprint std::vector command; - command.push_back("gpg"); + command.push_back(gpg_get_executable()); command.push_back("--batch"); command.push_back("--with-colons"); command.push_back("--list-secret-keys"); @@ -154,7 +164,7 @@ void gpg_encrypt_to_file (const std::string& filename, const std::string& recipi { // gpg --batch -o FILENAME -r RECIPIENT -e std::vector command; - command.push_back("gpg"); + command.push_back(gpg_get_executable()); command.push_back("--batch"); if (key_is_trusted) { command.push_back("--trust-model"); @@ -174,7 +184,7 @@ void gpg_decrypt_from_file (const std::string& filename, std::ostream& output) { // gpg -q -d FILENAME std::vector command; - command.push_back("gpg"); + command.push_back(gpg_get_executable()); command.push_back("-q"); command.push_back("-d"); command.push_back(filename); From 00a788748656c19f6462677491c191ac96032899 Mon Sep 17 00:00:00 2001 From: ticktockhouse Date: Wed, 31 May 2017 07:57:57 +0100 Subject: [PATCH 10/19] Fix tables in install.md Closes: #119 --- INSTALL.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 7fdb577..df28b70 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -2,19 +2,19 @@ To build git-crypt, you need: - | Debian/Ubuntu package | RHEL/CentOS package ----------------------------|-----------------------|------------------------ -Make | make | make -A C++ compiler (e.g. gcc) | g++ | gcc-c++ -OpenSSL development files | libssl-dev | openssl-devel +| Software | Debian/Ubuntu package | RHEL/CentOS package| +|---------------------------|-----------------------|--------------------| +|Make | make | make | +|A C++ compiler (e.g. gcc) | g++ | gcc-c++ | +|OpenSSL development files | libssl-dev | openssl-devel | To use git-crypt, you need: - | Debian/Ubuntu package | RHEL/CentOS package ----------------------------|-----------------------|------------------------ -Git 1.7.2 or newer | git | git -OpenSSL | openssl | openssl +| Software | Debian/Ubuntu package | RHEL/CentOS package | +|---------------------------|-----------------------|----------------------| +|Git 1.7.2 or newer | git | git | +|OpenSSL | openssl | openssl | Note: Git 1.8.5 or newer is recommended for best performance. From 2b1076108e60b36adc9610704b9f4eeabeb9a872 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 30 Oct 2016 15:57:14 -0700 Subject: [PATCH 11/19] Enable C++11 in Makefile --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 735d60f..68eb9db 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ # CXXFLAGS ?= -Wall -pedantic -Wno-long-long -O2 +CXXFLAGS += -std=c++11 PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin MANDIR ?= $(PREFIX)/share/man From edfa3dcb5ffc498e4f9962c0dc96fd45263faf54 Mon Sep 17 00:00:00 2001 From: Kevin Borgolte Date: Fri, 8 Jul 2016 17:54:28 -0700 Subject: [PATCH 12/19] Allow GPG to fail on some keys If multiple GPG keys exist that could be used to decrypt the repository key, but GPG fails on one of them (e.g., the first one because it is stored on a SmartCard that is not plugged in), then no other keys are used to try to decrypt it, failing entirely instead of trying the additional GPG keys. Modified-by: Andrew Ayer * Make exception variable const * Make whitespace conform to project conventions Signed-off-by: Andrew Ayer Closes: #88 --- commands.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/commands.cpp b/commands.cpp index 0972823..866eb81 100644 --- a/commands.cpp +++ b/commands.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include static std::string attribute_name (const char* key_name) @@ -589,13 +590,20 @@ static void load_key (Key_file& key_file, const char* key_name, const char* key_ static bool decrypt_repo_key (Key_file& key_file, const char* key_name, uint32_t key_version, const std::vector& secret_keys, const std::string& keys_path) { + std::exception_ptr gpg_error; + for (std::vector::const_iterator seckey(secret_keys.begin()); seckey != secret_keys.end(); ++seckey) { std::ostringstream path_builder; path_builder << keys_path << '/' << (key_name ? key_name : "default") << '/' << key_version << '/' << *seckey << ".gpg"; std::string path(path_builder.str()); if (access(path.c_str(), F_OK) == 0) { std::stringstream decrypted_contents; - gpg_decrypt_from_file(path, decrypted_contents); + try { + gpg_decrypt_from_file(path, decrypted_contents); + } catch (const Gpg_error&) { + gpg_error = std::current_exception(); + continue; + } Key_file this_version_key_file; this_version_key_file.load(decrypted_contents); const Key_file::Entry* this_version_entry = this_version_key_file.get(key_version); @@ -610,6 +618,11 @@ static bool decrypt_repo_key (Key_file& key_file, const char* key_name, uint32_t return true; } } + + if (gpg_error) { + std::rethrow_exception(gpg_error); + } + return false; } From d3bb5aba467194c4a08637e8730208177f138780 Mon Sep 17 00:00:00 2001 From: Adrian Cohea Date: Sat, 18 Mar 2017 17:23:34 -0600 Subject: [PATCH 13/19] Addresses -Wdeprecated-declarations warnings changing all references of std::auto_ptr to std::unique_ptr and changing the implementation of get_directory_contents() to use readdir, which is now reentrant, instead of readdir_r. Signed-off-by: Andrew Ayer Note: old implementations or readdir might not be re-entrant, but that's OK because git-crypt is not multi-threaded. --- crypto-openssl-10.cpp | 8 ++++---- crypto.hpp | 4 ++-- util-unix.cpp | 34 ++++++++++------------------------ 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/crypto-openssl-10.cpp b/crypto-openssl-10.cpp index 8cb10f7..f0f2c53 100644 --- a/crypto-openssl-10.cpp +++ b/crypto-openssl-10.cpp @@ -63,8 +63,8 @@ Aes_ecb_encryptor::Aes_ecb_encryptor (const unsigned char* raw_key) Aes_ecb_encryptor::~Aes_ecb_encryptor () { - // Note: Explicit destructor necessary because class contains an auto_ptr - // which contains an incomplete type when the auto_ptr is declared. + // Note: Explicit destructor necessary because class contains an unique_ptr + // which contains an incomplete type when the unique_ptr is declared. explicit_memset(&impl->key, '\0', sizeof(impl->key)); } @@ -86,8 +86,8 @@ Hmac_sha1_state::Hmac_sha1_state (const unsigned char* key, size_t key_len) Hmac_sha1_state::~Hmac_sha1_state () { - // Note: Explicit destructor necessary because class contains an auto_ptr - // which contains an incomplete type when the auto_ptr is declared. + // Note: Explicit destructor necessary because class contains an unique_ptr + // which contains an incomplete type when the unique_ptr is declared. HMAC_cleanup(&(impl->ctx)); } diff --git a/crypto.hpp b/crypto.hpp index db03241..a7d7edb 100644 --- a/crypto.hpp +++ b/crypto.hpp @@ -57,7 +57,7 @@ public: private: struct Aes_impl; - std::auto_ptr impl; + std::unique_ptr impl; public: Aes_ecb_encryptor (const unsigned char* key); @@ -102,7 +102,7 @@ public: private: struct Hmac_impl; - std::auto_ptr impl; + std::unique_ptr impl; public: Hmac_sha1_state (const unsigned char* key, size_t key_len); diff --git a/util-unix.cpp b/util-unix.cpp index 6bea324..75ba924 100644 --- a/util-unix.cpp +++ b/util-unix.cpp @@ -179,19 +179,6 @@ int util_rename (const char* from, const char* to) return rename(from, to); } -static size_t sizeof_dirent_for (DIR* p) -{ - long name_max = fpathconf(dirfd(p), _PC_NAME_MAX); - if (name_max == -1) { - #ifdef NAME_MAX - name_max = NAME_MAX; - #else - name_max = 255; - #endif - } - return offsetof(struct dirent, d_name) + name_max + 1; // final +1 is for d_name's null terminator -} - std::vector get_directory_contents (const char* path) { std::vector contents; @@ -201,19 +188,18 @@ std::vector get_directory_contents (const char* path) throw System_error("opendir", path, errno); } try { - std::vector buffer(sizeof_dirent_for(dir)); - struct dirent* dirent_buffer = reinterpret_cast(&buffer[0]); struct dirent* ent = NULL; - int err = 0; - while ((err = readdir_r(dir, dirent_buffer, &ent)) == 0 && ent != NULL) { - if (std::strcmp(ent->d_name, ".") == 0 || std::strcmp(ent->d_name, "..") == 0) { - continue; - } - contents.push_back(ent->d_name); - } - if (err != 0) { - throw System_error("readdir_r", path, errno); + + errno = 0; + + while((ent = readdir(dir)) != NULL && errno == 0) { + if (std::strcmp(ent->d_name, ".") && std::strcmp(ent->d_name, "..")) + contents.push_back(ent->d_name); } + + if(errno) + throw System_error("readdir", path, errno); + } catch (...) { closedir(dir); throw; From 0e4ad51a13f1614910cf536fc2e2a7ae484112d2 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 10 Sep 2017 12:29:43 -0700 Subject: [PATCH 14/19] Update a comment regarding unique_ptr --- crypto-openssl-11.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto-openssl-11.cpp b/crypto-openssl-11.cpp index ef85eb5..ad87b7d 100644 --- a/crypto-openssl-11.cpp +++ b/crypto-openssl-11.cpp @@ -63,8 +63,8 @@ Aes_ecb_encryptor::Aes_ecb_encryptor (const unsigned char* raw_key) Aes_ecb_encryptor::~Aes_ecb_encryptor () { - // Note: Explicit destructor necessary because class contains an auto_ptr - // which contains an incomplete type when the auto_ptr is declared. + // Note: Explicit destructor necessary because class contains an unique_ptr + // which contains an incomplete type when the unique_ptr is declared. explicit_memset(&impl->key, '\0', sizeof(impl->key)); } From f03fdc6ad037c026b0f435f4deefa8ea1ccb8aef Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 10 Sep 2017 12:33:18 -0700 Subject: [PATCH 15/19] Clean up readdir code, add a comment about why we're using readdir --- util-unix.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/util-unix.cpp b/util-unix.cpp index 75ba924..1f739ac 100644 --- a/util-unix.cpp +++ b/util-unix.cpp @@ -188,17 +188,22 @@ std::vector get_directory_contents (const char* path) throw System_error("opendir", path, errno); } try { - struct dirent* ent = NULL; - errno = 0; - - while((ent = readdir(dir)) != NULL && errno == 0) { - if (std::strcmp(ent->d_name, ".") && std::strcmp(ent->d_name, "..")) + // Note: readdir is reentrant in new implementations. In old implementations, + // it might not be, but git-crypt isn't multi-threaded so that's OK. + // We don't use readdir_r because it's buggy and deprecated: + // https://womble.decadent.org.uk/readdir_r-advisory.html + // http://austingroupbugs.net/view.php?id=696 + // http://man7.org/linux/man-pages/man3/readdir_r.3.html + while (struct dirent* ent = readdir(dir)) { + if (!(std::strcmp(ent->d_name, ".") == 0 || std::strcmp(ent->d_name, "..") == 0)) { contents.push_back(ent->d_name); + } } - if(errno) + if (errno) { throw System_error("readdir", path, errno); + } } catch (...) { closedir(dir); From ccdcc76f8e1a639847a8accd801f5a284194e43f Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 10 Sep 2017 12:36:21 -0700 Subject: [PATCH 16/19] Switch from NULL to nullptr --- commands.cpp | 6 +++--- coprocess-unix.cpp | 10 ++++----- coprocess-win32.cpp | 50 +++++++++++++++++++++---------------------- crypto-openssl-11.cpp | 2 +- util-unix.cpp | 4 ++-- util-win32.cpp | 12 +++++------ 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/commands.cpp b/commands.cpp index 866eb81..5ac0b47 100644 --- a/commands.cpp +++ b/commands.cpp @@ -488,7 +488,7 @@ static bool check_if_file_is_encrypted (const std::string& filename) static bool is_git_file_mode (const std::string& mode) { - return (std::strtoul(mode.c_str(), NULL, 8) & 0170000) == 0100000; + return (std::strtoul(mode.c_str(), nullptr, 8) & 0170000) == 0100000; } static void get_encrypted_files (std::vector& files, const char* key_name) @@ -509,8 +509,8 @@ static void get_encrypted_files (std::vector& files, const char* ke ls_files.spawn(ls_files_command); Coprocess check_attr; - std::ostream* check_attr_stdin = NULL; - std::istream* check_attr_stdout = NULL; + std::ostream* check_attr_stdin = nullptr; + std::istream* check_attr_stdout = nullptr; if (git_version() >= make_version(1, 8, 5)) { // In Git 1.8.5 (released 27 Nov 2013) and higher, we use a single `git check-attr` process // to get the attributes of all files at once. In prior versions, we have to fork and exec diff --git a/coprocess-unix.cpp b/coprocess-unix.cpp index f9577e5..45be332 100644 --- a/coprocess-unix.cpp +++ b/coprocess-unix.cpp @@ -41,7 +41,7 @@ static int execvp (const std::string& file, const std::vector& args for (std::vector::const_iterator arg(args.begin()); arg != args.end(); ++arg) { args_c_str.push_back(arg->c_str()); } - args_c_str.push_back(NULL); + args_c_str.push_back(nullptr); return execvp(file.c_str(), const_cast(&args_c_str[0])); } @@ -50,10 +50,10 @@ Coprocess::Coprocess () pid = -1; stdin_pipe_reader = -1; stdin_pipe_writer = -1; - stdin_pipe_ostream = NULL; + stdin_pipe_ostream = nullptr; stdout_pipe_reader = -1; stdout_pipe_writer = -1; - stdout_pipe_istream = NULL; + stdout_pipe_istream = nullptr; } Coprocess::~Coprocess () @@ -79,7 +79,7 @@ std::ostream* Coprocess::stdin_pipe () void Coprocess::close_stdin () { delete stdin_pipe_ostream; - stdin_pipe_ostream = NULL; + stdin_pipe_ostream = nullptr; if (stdin_pipe_writer != -1) { close(stdin_pipe_writer); stdin_pipe_writer = -1; @@ -107,7 +107,7 @@ std::istream* Coprocess::stdout_pipe () void Coprocess::close_stdout () { delete stdout_pipe_istream; - stdout_pipe_istream = NULL; + stdout_pipe_istream = nullptr; if (stdout_pipe_writer != -1) { close(stdout_pipe_writer); stdout_pipe_writer = -1; diff --git a/coprocess-win32.cpp b/coprocess-win32.cpp index 46e21d0..556c873 100644 --- a/coprocess-win32.cpp +++ b/coprocess-win32.cpp @@ -96,14 +96,14 @@ static HANDLE spawn_command (const std::vector& command, HANDLE std std::string cmdline(format_cmdline(command)); - if (!CreateProcessA(NULL, // application name (NULL to use command line) + if (!CreateProcessA(nullptr, // application name (nullptr to use command line) const_cast(cmdline.c_str()), - NULL, // process security attributes - NULL, // primary thread security attributes + nullptr, // process security attributes + nullptr, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags - NULL, // use parent's environment - NULL, // use parent's current directory + nullptr, // use parent's environment + nullptr, // use parent's current directory &start_info, &proc_info)) { throw System_error("CreateProcess", cmdline, GetLastError()); @@ -117,13 +117,13 @@ static HANDLE spawn_command (const std::vector& command, HANDLE std Coprocess::Coprocess () { - proc_handle = NULL; - stdin_pipe_reader = NULL; - stdin_pipe_writer = NULL; - stdin_pipe_ostream = NULL; - stdout_pipe_reader = NULL; - stdout_pipe_writer = NULL; - stdout_pipe_istream = NULL; + proc_handle = nullptr; + stdin_pipe_reader = nullptr; + stdin_pipe_writer = nullptr; + stdin_pipe_ostream = nullptr; + stdout_pipe_reader = nullptr; + stdout_pipe_writer = nullptr; + stdout_pipe_istream = nullptr; } Coprocess::~Coprocess () @@ -143,7 +143,7 @@ std::ostream* Coprocess::stdin_pipe () // Set the bInheritHandle flag so pipe handles are inherited. sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES); sec_attr.bInheritHandle = TRUE; - sec_attr.lpSecurityDescriptor = NULL; + sec_attr.lpSecurityDescriptor = nullptr; // Create a pipe for the child process's STDIN. if (!CreatePipe(&stdin_pipe_reader, &stdin_pipe_writer, &sec_attr, 0)) { @@ -163,14 +163,14 @@ std::ostream* Coprocess::stdin_pipe () void Coprocess::close_stdin () { delete stdin_pipe_ostream; - stdin_pipe_ostream = NULL; + stdin_pipe_ostream = nullptr; if (stdin_pipe_writer) { CloseHandle(stdin_pipe_writer); - stdin_pipe_writer = NULL; + stdin_pipe_writer = nullptr; } if (stdin_pipe_reader) { CloseHandle(stdin_pipe_reader); - stdin_pipe_reader = NULL; + stdin_pipe_reader = nullptr; } } @@ -182,7 +182,7 @@ std::istream* Coprocess::stdout_pipe () // Set the bInheritHandle flag so pipe handles are inherited. sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES); sec_attr.bInheritHandle = TRUE; - sec_attr.lpSecurityDescriptor = NULL; + sec_attr.lpSecurityDescriptor = nullptr; // Create a pipe for the child process's STDOUT. if (!CreatePipe(&stdout_pipe_reader, &stdout_pipe_writer, &sec_attr, 0)) { @@ -202,27 +202,27 @@ std::istream* Coprocess::stdout_pipe () void Coprocess::close_stdout () { delete stdout_pipe_istream; - stdout_pipe_istream = NULL; + stdout_pipe_istream = nullptr; if (stdout_pipe_writer) { CloseHandle(stdout_pipe_writer); - stdout_pipe_writer = NULL; + stdout_pipe_writer = nullptr; } if (stdout_pipe_reader) { CloseHandle(stdout_pipe_reader); - stdout_pipe_reader = NULL; + stdout_pipe_reader = nullptr; } } void Coprocess::spawn (const std::vector& args) { - proc_handle = spawn_command(args, stdin_pipe_reader, stdout_pipe_writer, NULL); + proc_handle = spawn_command(args, stdin_pipe_reader, stdout_pipe_writer, nullptr); if (stdin_pipe_reader) { CloseHandle(stdin_pipe_reader); - stdin_pipe_reader = NULL; + stdin_pipe_reader = nullptr; } if (stdout_pipe_writer) { CloseHandle(stdout_pipe_writer); - stdout_pipe_writer = NULL; + stdout_pipe_writer = nullptr; } } @@ -243,7 +243,7 @@ int Coprocess::wait () size_t Coprocess::write_stdin (void* handle, const void* buf, size_t count) { DWORD bytes_written; - if (!WriteFile(static_cast(handle)->stdin_pipe_writer, buf, count, &bytes_written, NULL)) { + if (!WriteFile(static_cast(handle)->stdin_pipe_writer, buf, count, &bytes_written, nullptr)) { throw System_error("WriteFile", "", GetLastError()); } return bytes_written; @@ -257,7 +257,7 @@ size_t Coprocess::read_stdout (void* handle, void* buf, size_t count) // fails with ERROR_BROKEN_PIPE. DWORD bytes_read; do { - if (!ReadFile(static_cast(handle)->stdout_pipe_reader, buf, count, &bytes_read, NULL)) { + if (!ReadFile(static_cast(handle)->stdout_pipe_reader, buf, count, &bytes_read, nullptr)) { const DWORD read_error = GetLastError(); if (read_error != ERROR_BROKEN_PIPE) { throw System_error("ReadFile", "", read_error); diff --git a/crypto-openssl-11.cpp b/crypto-openssl-11.cpp index ad87b7d..adf03bb 100644 --- a/crypto-openssl-11.cpp +++ b/crypto-openssl-11.cpp @@ -83,7 +83,7 @@ Hmac_sha1_state::Hmac_sha1_state (const unsigned char* key, size_t key_len) { impl->ctx = HMAC_CTX_new(); - HMAC_Init_ex(impl->ctx, key, key_len, EVP_sha1(), NULL); + HMAC_Init_ex(impl->ctx, key, key_len, EVP_sha1(), nullptr); } Hmac_sha1_state::~Hmac_sha1_state () diff --git a/util-unix.cpp b/util-unix.cpp index 1f739ac..3f23d44 100644 --- a/util-unix.cpp +++ b/util-unix.cpp @@ -132,7 +132,7 @@ std::string our_exe_path () return argv0; } else if (std::strchr(argv0, '/')) { // argv[0] contains / => it a relative path that should be resolved - char* resolved_path_p = realpath(argv0, NULL); + char* resolved_path_p = realpath(argv0, nullptr); std::string resolved_path(resolved_path_p); free(resolved_path_p); return resolved_path; @@ -149,7 +149,7 @@ int exit_status (int wait_status) void touch_file (const std::string& filename) { - if (utimes(filename.c_str(), NULL) == -1 && errno != ENOENT) { + if (utimes(filename.c_str(), nullptr) == -1 && errno != ENOENT) { throw System_error("utimes", filename, errno); } } diff --git a/util-win32.cpp b/util-win32.cpp index 445d185..4c47a2d 100644 --- a/util-win32.cpp +++ b/util-win32.cpp @@ -46,12 +46,12 @@ std::string System_error::message () const LPTSTR error_message; FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, + nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&error_message), 0, - NULL); + nullptr); mesg += error_message; LocalFree(error_message); } @@ -100,7 +100,7 @@ void mkdir_parent (const std::string& path) std::string prefix(path.substr(0, slash)); if (GetFileAttributes(prefix.c_str()) == INVALID_FILE_ATTRIBUTES) { // prefix does not exist, so try to create it - if (!CreateDirectory(prefix.c_str(), NULL)) { + if (!CreateDirectory(prefix.c_str(), nullptr)) { throw System_error("CreateDirectory", prefix, GetLastError()); } } @@ -114,7 +114,7 @@ std::string our_exe_path () std::vector buffer(128); size_t len; - while ((len = GetModuleFileNameA(NULL, &buffer[0], buffer.size())) == buffer.size()) { + while ((len = GetModuleFileNameA(nullptr, &buffer[0], buffer.size())) == buffer.size()) { // buffer may have been truncated - grow and try again buffer.resize(buffer.size() * 2); } @@ -132,7 +132,7 @@ int exit_status (int status) void touch_file (const std::string& filename) { - HANDLE fh = CreateFileA(filename.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + HANDLE fh = CreateFileA(filename.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (fh == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); if (error == ERROR_FILE_NOT_FOUND) { @@ -146,7 +146,7 @@ void touch_file (const std::string& filename) FILETIME file_time; SystemTimeToFileTime(&system_time, &file_time); - if (!SetFileTime(fh, NULL, NULL, &file_time)) { + if (!SetFileTime(fh, nullptr, nullptr, &file_time)) { DWORD error = GetLastError(); CloseHandle(fh); throw System_error("SetFileTime", filename, error); From f3dd69e4c76c9315a337294b41bdb4c22084bbe8 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 26 Nov 2017 10:05:42 -0800 Subject: [PATCH 17/19] Update THANKS file --- THANKS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/THANKS.md b/THANKS.md index 4c0d53e..eaeb789 100644 --- a/THANKS.md +++ b/THANKS.md @@ -12,6 +12,10 @@ For their contributions to git-crypt, I thank: * Linus G Thiel * Michael Schout * Simon Kotlinski + * Kevin Menard + * Wael M. Nasreddine + * Kevin Borgolte + * Adrian Cohea * And everyone who has tested git-crypt, provided feedback, reported bugs, and participated in discussions about new features. From 37df6fb5ad2a258858061d776eeca28e9f922fd2 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 26 Nov 2017 10:21:23 -0800 Subject: [PATCH 18/19] Update INSTALL file to reflect C++11 requirement --- INSTALL | 14 +++++++------- INSTALL.md | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/INSTALL b/INSTALL index 41049f1..e6c9471 100644 --- a/INSTALL +++ b/INSTALL @@ -2,19 +2,19 @@ DEPENDENCIES To build git-crypt, you need: - Debian/Ubuntu package RHEL/CentOS package + Debian/Ubuntu package RHEL/CentOS package ----------------------------------------------------------------------------- - Make make make - A C++ compiler (e.g. gcc) g++ gcc-c++ - OpenSSL development files libssl-dev openssl-devel + Make make make + A C++11 compiler (e.g. gcc 4.9+) g++ gcc-c++ + OpenSSL development files libssl-dev openssl-devel To use git-crypt, you need: - Debian/Ubuntu package RHEL/CentOS package + Debian/Ubuntu package RHEL/CentOS package ----------------------------------------------------------------------------- - Git 1.7.2 or newer git git - OpenSSL openssl openssl + Git 1.7.2 or newer git git + OpenSSL openssl openssl Note: Git 1.8.5 or newer is recommended for best performance. diff --git a/INSTALL.md b/INSTALL.md index df28b70..5a04138 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -2,19 +2,19 @@ To build git-crypt, you need: -| Software | Debian/Ubuntu package | RHEL/CentOS package| -|---------------------------|-----------------------|--------------------| -|Make | make | make | -|A C++ compiler (e.g. gcc) | g++ | gcc-c++ | -|OpenSSL development files | libssl-dev | openssl-devel | +| Software | Debian/Ubuntu package | RHEL/CentOS package| +|---------------------------------|-----------------------|--------------------| +|Make | make | make | +|A C++11 compiler (e.g. gcc 4.9+) | g++ | gcc-c++ | +|OpenSSL development files | libssl-dev | openssl-devel | To use git-crypt, you need: -| Software | Debian/Ubuntu package | RHEL/CentOS package | -|---------------------------|-----------------------|----------------------| -|Git 1.7.2 or newer | git | git | -|OpenSSL | openssl | openssl | +| Software | Debian/Ubuntu package | RHEL/CentOS package| +|---------------------------------|-----------------------|--------------------| +|Git 1.7.2 or newer | git | git | +|OpenSSL | openssl | openssl | Note: Git 1.8.5 or newer is recommended for best performance. From 546664f152ec0441dd46700eb06e63a0414ec877 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 26 Nov 2017 10:24:03 -0800 Subject: [PATCH 19/19] Prepare 0.6.0 release --- NEWS | 13 +++++++++++++ NEWS.md | 13 +++++++++++++ README | 2 +- README.md | 4 ++-- git-crypt.hpp | 2 +- man/git-crypt.xml | 4 ++-- 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index d01a975..41e2bd5 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,16 @@ +v0.6.0 (2017-11-26) + * Add support for OpenSSL 1.1 (still works with OpenSSL 1.0). + * Switch to C++11 (gcc 4.9 or higher now required to build). + * Allow GPG to fail on some keys (makes unlock work better if there are + multiple keys that can unlock the repo but only some are available). + * Allow the repo state directory to be configured with the + git-crypt.repoStateDir git config option. + * Respect the gpg.program git config option. + * Don't hard code path to git-crypt in .git/config on Linux (ensures + repo continues to work if git-crypt is moved). + * Ensure git-crypt's gpg files won't be treated as text by Git. + * Minor improvements to build system, documentation. + v0.5.0 (2015-05-30) * Drastically speed up lock/unlock when used with Git 1.8.5 or newer. * Add git-crypt(1) man page (pass ENABLE_MAN=yes to make to build). diff --git a/NEWS.md b/NEWS.md index dd8772f..080035f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,19 @@ News ==== +######v0.6.0 (2017-11-26) +* Add support for OpenSSL 1.1 (still works with OpenSSL 1.0). +* Switch to C++11 (gcc 4.9 or higher now required to build). +* Allow GPG to fail on some keys (makes unlock work better if there are + multiple keys that can unlock the repo but only some are available). +* Allow the repo state directory to be configured with the + git-crypt.repoStateDir git config option. +* Respect the gpg.program git config option. +* Don't hard code path to git-crypt in .git/config on Linux (ensures + repo continues to work if git-crypt is moved). +* Ensure git-crypt's gpg files won't be treated as text by Git. +* Minor improvements to build system, documentation. + ######v0.5.0 (2015-05-30) * Drastically speed up lock/unlock when used with Git 1.8.5 or newer. * Add git-crypt(1) man page (pass `ENABLE_MAN=yes` to make to build). diff --git a/README b/README index 2bdc1c6..8c4177c 100644 --- a/README +++ b/README @@ -69,7 +69,7 @@ encryption and decryption happen transparently. CURRENT STATUS -The latest version of git-crypt is 0.5.0, released on 2015-05-30. +The latest version of git-crypt is 0.6.0, released on 2017-11-26. git-crypt aims to be bug-free and reliable, meaning it shouldn't crash, malfunction, or expose your confidential data. However, it has not yet reached maturity, meaning it is not as documented, diff --git a/README.md b/README.md index 92c69ca..bd0592b 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,8 @@ encryption and decryption happen transparently. Current Status -------------- -The latest version of git-crypt is [0.5.0](NEWS.md), released on -2015-05-30. git-crypt aims to be bug-free and reliable, meaning it +The latest version of git-crypt is [0.6.0](NEWS.md), released on +2017-11-26. git-crypt aims to be bug-free and reliable, meaning it shouldn't crash, malfunction, or expose your confidential data. However, it has not yet reached maturity, meaning it is not as documented, featureful, or easy-to-use as it should be. Additionally, diff --git a/git-crypt.hpp b/git-crypt.hpp index bb4beaa..ce41dfa 100644 --- a/git-crypt.hpp +++ b/git-crypt.hpp @@ -31,7 +31,7 @@ #ifndef GIT_CRYPT_GIT_CRYPT_HPP #define GIT_CRYPT_GIT_CRYPT_HPP -#define VERSION "0.5.0" +#define VERSION "0.6.0" extern const char* argv0; // initialized in main() to argv[0] diff --git a/man/git-crypt.xml b/man/git-crypt.xml index 7a20569..96f53d7 100644 --- a/man/git-crypt.xml +++ b/man/git-crypt.xml @@ -7,8 +7,8 @@ --> git-crypt - 2015-05-30 - git-crypt 0.5.0 + 2017-11-26 + git-crypt 0.6.0 Andrew Ayer