From 1ed25d84905ed4cc907a7e9f9c4f20eae5deaba1 Mon Sep 17 00:00:00 2001 From: fzxx <53783792+fzxx@users.noreply.github.com> Date: Wed, 19 Aug 2026 12:35:24 +0800 Subject: [PATCH] Added xchacha20-poly1305 cipher algorithm , Fixed translations. --- .gitignore | 1 + INSTALL.md | 2 +- README_zh-CN.md | 33 +++--- changelog.md | 5 + cppcryptfs/cppcryptfs.vcxproj | 12 +-- cppcryptfs/res/en/Resources_EN.rc | Bin 63316 -> 63698 bytes cppcryptfs/res/ru/Resources_RU.rc | Bin 68134 -> 68490 bytes cppcryptfs/res/zh-CN/Resources_ZH-CN.rc | Bin 54274 -> 54566 bytes cppcryptfs/res/zh-CN/resource.h | 2 + cppcryptfs/resource.h | Bin 26152 -> 26316 bytes cppcryptfs/ui/CreatePropertyPage.cpp | 9 +- cppcryptfs/ui/locutils.cpp | 15 ++- cppcryptfsctl/cppcryptfsctl.cpp | 15 ++- cppcryptfsctl/cppcryptfsctl.vcxproj | 10 +- libcommonutil/libcommonutil.vcxproj | 4 +- libcppcryptfs/config/cryptconfig.cpp | 95 ++++++++++++++---- libcppcryptfs/config/cryptconfig.h | 23 ++++- libcppcryptfs/context/cryptcontext.cpp | 3 +- libcppcryptfs/crypt/chacha20.cpp | 106 ++++++++++++++++++++ libcppcryptfs/crypt/chacha20.h | 35 +++++++ libcppcryptfs/crypt/crypt.cpp | 105 ++++++++++++------- libcppcryptfs/crypt/crypt.h | 7 +- libcppcryptfs/crypt/cryptdefs.h | 2 + libcppcryptfs/file/cryptfile.cpp | 83 +++++++-------- libcppcryptfs/file/cryptio.cpp | 63 +++++++----- libcppcryptfs/file/iobufferpool.h | 6 +- libcppcryptfs/filename/cryptfilename.cpp | 2 +- libcppcryptfs/libcppcryptfs.vcxproj | 18 ++-- libcppcryptfs/libcppcryptfs.vcxproj.filters | 2 +- libcppcryptfs/util/fileutil.cpp | 36 +++---- libcppcryptfs/util/fileutil.h | 13 ++- libcppcryptfs/util/util.cpp | 24 +++-- libcppcryptfs/util/wintypes.h | 100 +++++++++--------- libipc/libipc.vcxproj | 4 +- version.txt | 2 +- 35 files changed, 580 insertions(+), 257 deletions(-) create mode 100644 libcppcryptfs/crypt/chacha20.cpp create mode 100644 libcppcryptfs/crypt/chacha20.h diff --git a/.gitignore b/.gitignore index 889d6645..561daa0b 100755 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ libcommonutil/libcommonutil.vcxproj.user libcppcryptfs/libcppcryptfs.vcxproj.user cppcryptfs/gitversion.rc2 cppcryptfsctl/gitversion.rc2 +VersionGenerated.h diff --git a/INSTALL.md b/INSTALL.md index 532fd3b2..2b0fab73 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -87,7 +87,7 @@ To start an elevated command prompt in Windows 10, click on the search (magnifyi Whether or not you are using git, everything will go easier if you put everything in c:\git. Getting OpenSSL from github will get you the latest development version of OpenSSL. However, the cppcryptfs releases are built using the -current 3.0.x Long Term Support (LTS) version of OpenSSL (currently openssl-3.0.13). These are available from https://www.openssl.org/source/. To use those, you will need to download +current 3.0.x Long Term Support (LTS) version of OpenSSL (currently openssl-3.0.21). These are available from https://www.openssl.org/source/. To use those, you will need to download the .tar.gz and extract it into a directory. You can build it with the same instructions that follow regardless of how you get OpenSSL. Microsoft has announced a compiler-based mitigation for one variant of the Spectre vulnerability. To use it, you need to have version 15.5 or higher of Visual Studio. To use the mitigation with OpenSSL, you need to add the /Qspectre flag to the compiler optimization flags. OpenSSL currently does not use this flag. Also, to be extra safe, add the /guard:cf (Control Flow Guard) flag. To use these, you need to edit c:\\git\openssl\\Configurations\\10-main.conf and change "/O2" to "/O2 /Qspectre /guard:cf" (omit /guard:cf if building for ARM64). diff --git a/README_zh-CN.md b/README_zh-CN.md index f0e0bd16..e7ebddef 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -12,7 +12,7 @@ cppcryptfs 是 gocryptfs 文件系统在 Windows 上的 C++ 实现。cppcryptfs cppcryptfs 提供对虚拟文件系统中的文件和文件名进行实时、静态和云端加密。它使用 [Dokany](https://github.com/dokan-dev/dokany) 驱动程序和库在 Windows 下的用户模式中提供虚拟文件系统。 -**重要提示**:根据 [此](https://github.com/rfjakob/gocryptfs/issues/50) 讨论,如果你的密码很强,你才应该将创建的 gocryptfs.conf 存储在云端。 +**重要提示**:根据 [此](https://github.com/rfjakob/gocryptfs/issues/50) 讨论,只有你的密码足够强,才应该将创建的 gocryptfs.conf 存储在云端。 如果攻击者获取了你的 gocryptfs.conf,那么他们将能够动用他们拥有的任何资源对你加密的密码进行暴力破解。增加 scryptn 会减慢他们的破解速度(见下文)。开发者将他的 gocryptfs.conf 存储在云端并使用非常强的密码。 你可以使用 cppcryptfs 在一个文件夹中创建一个加密文件系统。这个加密文件系统由你选择的密码保护。 @@ -67,7 +67,7 @@ cppcryptfs 在没有管理员权限运行时失败的测试与对 DACL(自由 在 [INSTALL.md](INSTALL.md) 中有详细的构建说明。 -cppcryptfs 当前与 Dokany 2.3.0.1000 保持同步。 +cppcryptfs 当前与 Dokany 2.3.1.1000 保持同步。 使用方法 ------- @@ -199,13 +199,13 @@ cppcryptfs 会缓存有关文件系统的信息。如果缓存中的某个条目 如果你不在两个同时运行的 cppcryptfs 实例之间或 cppcryptfs 实例与 gocryptfs 实例之间同步文件系统,那么没有理由不将缓存生存时间设置为较高的值或无限期。 -**多线程** +**Multi-threaded(多线程)** 如果启用此选项,则每个文件系统可以有多个线程处理请求。这可能会提高性能。Dokany 会自动选择要使用的线程数。 如果禁用此选项,则每个文件系统将仅使用一个线程来处理请求。 -**不区分大小写** +**Case insensitive(不区分大小写)** 此选项仅在前向模式下且使用加密文件名时有效。使用加密文件名的反向模式文件系统始终区分大小写,而使用纯文本文件名的文件系统始终不区分大小写。 @@ -215,7 +215,7 @@ cppcryptfs 会缓存有关文件系统的信息。如果缓存中的某个条目 有关更多信息,请参阅“大小写敏感性”部分。 -**启用挂载管理器(回收站)** +**Enable Mount Manager (Recycle Bin)(启用挂载管理器(回收站))** 当选择默认设置或推荐设置时,此设置当前未启用。如果你希望使用它,则必须单独启用。此设置尚未经过全面测试。 @@ -266,7 +266,7 @@ cppcryptfs --list=d:\ 在默认设置和推荐设置中,此设置均未启用。 -**在内存中加密密钥** +**Encrypt Keys in Memory(在内存中加密密钥)** 启用此设置后,cppcryptfs 在不需要加密密钥(主密钥和任何派生密钥)时,会使用 Windows 数据保护 API (DPAPI) 对其进行加密。密钥使用 DPAPI 进行加密,在需要时进行解密,然后在不需要时将未加密的副本清零。有关 DPAPI 的更多信息,请参阅下面的“保存的密码”部分。 @@ -280,7 +280,7 @@ cppcryptfs --list=d:\ 无论是默认设置还是推荐设置,都未启用此设置。 -**在内存中缓存密钥** +**Cache Keys in Memory(在内存中缓存密钥)** 除非启用“在内存中加密密钥”,否则此设置无效。 @@ -306,7 +306,7 @@ Dokany 通常需要 5 秒才能回调。然而,文件系统几乎会立即显 此设置默认启用。 -**卸载时若正在使用则发出警告** +**Warn if in use when dismounting(卸载时若正在使用则发出警告)** 如果启用此设置,当用户尝试卸载文件系统、卸载所有文件系统或退出程序时,如果挂载的文件系统上仍有任何打开的文件或目录,cppcryptfs 将通知用户并询问是否应继续卸载。 @@ -314,7 +314,7 @@ Dokany 通常需要 5 秒才能回调。然而,文件系统几乎会立即显 无论是默认设置还是推荐设置,都未启用此设置。 -**拒绝其他会话访问** +**Deny Other Sessions(拒绝其他会话)** 如果启用此设置,加密卷将仅在启动挂载它们的 cppcryptfs 实例的会话中可访问。用于挂载的任何驱动器号在其他会话中仍然可见,但其他会话无法访问这些驱动器。 @@ -322,11 +322,11 @@ Dokany 通常需要 5 秒才能回调。然而,文件系统几乎会立即显 此功能的测试范围让开发人员相信,此设置可以使挂载的卷免受共享同一台计算机并登录到不同会话的其他普通登录用户的访问。然而,不能确定一个有决心且有知识的攻击者无法找到绕过此保护的方法。 -有关更多信息,请参阅下面的“拒绝服务访问”说明。 +有关更多信息,请参阅下面的“Deny Services(拒绝服务)”说明。 无论是默认设置还是推荐设置,都未启用此设置。 -**拒绝服务访问** +**Deny Services(拒绝服务)** 如果启用此设置,加密卷将无法被在会话 0 中运行的 Windows 服务访问。 @@ -346,7 +346,7 @@ Dokany 通常需要 5 秒才能回调。然而,文件系统几乎会立即显 更多设置 --------- -还有一个设置选项卡。 +还有一个更多设置选项卡。 ![Alt text](/screenshots/screenshot_more_settings.png?raw=true "设置选项卡") *显示推荐设置* @@ -365,6 +365,10 @@ Dokany 通常需要 5 秒才能回调。然而,文件系统几乎会立即显 如果任何条件为真,则此设置开启,因此若要始终启用它,可以同时勾选“NTFS”和“非 NTFS”框。 +**界面语言** + +在这里你可以更改界面的语言。更改之后,必须转到挂载选项卡并退出程序,然后重新启动程序才能使更改生效。 + 保存的密码 ------ @@ -414,7 +418,8 @@ https://msdn.microsoft.com/en-us/library/ms995355.aspx 可以以正向模式挂载已挂载的反向文件系统。正向文件系统也将是只读的,这主要用于测试。 -### 命令行选项 +命令行选项 +------ cppcryptfs 接受一些用于挂载和卸载文件系统的命令行选项。目前,只能通过图形用户界面 (GUI) 创建文件系统。 cppcryptfs 只能有一个主实例运行。如果没有其他 cppcryptfs 实例正在运行,那么 cppcryptfs 会处理命令行参数,然后继续运行。如果已经有另一个 cppcryptfs 实例正在运行,那么 cppcryptfs 会将其命令行参数发送给正在运行的主实例。如果从控制台窗口运行,它会将处理命令行的任何输出打印到控制台。如果不是从控制台运行,它会在消息框中显示输出。 @@ -427,8 +432,6 @@ cppcryptfsctl 在成功时将 ERRORLEVEL 设置为 0,发生错误时设置为 命名管道的名称会用启动 cppcryptfs 的用户的用户名和域名进行修饰。因此,cppcryptfs/cppcryptfsctl 只能与同一用户启动的 cppcryptfs 实例进行通信。 -### 使用方法 - ``` 用法:cppcryptfs/cppcryptfsctl [选项] diff --git a/changelog.md b/changelog.md index 004799d6..0efb4729 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,11 @@ Changelog ------------ +v1.4.4.11 2 Sep 2026 +* Added xchacha20-poly1305 cipher algorithm. +* Fixed a path out-of-bounds bug when checking a trailing path separator. +* Fixed translations. + v1.4.4.10 25 Apr 2026 * Added ARM64 build. diff --git a/cppcryptfs/cppcryptfs.vcxproj b/cppcryptfs/cppcryptfs.vcxproj index 77827eea..8e7648e5 100755 --- a/cppcryptfs/cppcryptfs.vcxproj +++ b/cppcryptfs/cppcryptfs.vcxproj @@ -1,4 +1,4 @@ - + @@ -195,7 +195,7 @@ Use Level3 Disabled - _WINDOWS;_DEBUG;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) true $(PROJECTDIR)..\libcppcryptfs;C:\Program Files\Dokan\Dokan Library-2.3.1\include;$(ProjectDir);c:\git\rapidjson\include;C:\git\openssl-arm64-static\include false @@ -254,7 +254,7 @@ $(IntDir);$(ProjectDir);$(ProjectDir)res\en;$(ProjectDir)res\ru;%(AdditionalIncludeDirectories) - python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir) + python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir)" @@ -291,7 +291,7 @@ $(IntDir);$(ProjectDir);$(ProjectDir)res\en;$(ProjectDir)res\ru;%(AdditionalIncludeDirectories) - python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir) + python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir)" @@ -301,7 +301,7 @@ MaxSpeed true true - _WINDOWS;NDEBUG;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) true $(PROJECTDIR)..\libcppcryptfs;C:\Program Files\Dokan\Dokan Library-2.3.1\include;$(ProjectDir);c:\git\rapidjson\include;C:\git\openssl-arm64-static\include /Qspectre @@ -328,7 +328,7 @@ $(IntDir);$(ProjectDir);$(ProjectDir)res\en;$(ProjectDir)res\ru;%(AdditionalIncludeDirectories) - python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir) + python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir)" diff --git a/cppcryptfs/res/en/Resources_EN.rc b/cppcryptfs/res/en/Resources_EN.rc index 7d5f7851a75a0fecd7443da4a4d145a38a6c6c8f..f8198f40f79e2e591c2bb5cc9a2c4989df4a0fe6 100644 GIT binary patch delta 348 zcmccej``9@<_&T@lUL2rm|Rn=v^j%E#BOq23D4#>FP(X&3JehF$>72e%n;Au%;3S` z2qohgf*4#G0vSRXJi#oGya$6fgUjTLC#5}=7$Sh`G8ht}q!EJwgDyh=Lq0W3~rP6UY2%_VaQ`pV&G-qg1ScmXeTxo zIRaf241}HxVPMz#Gx#yMG57%a&XXNw#FL42RWi`sc|e#Cbd3T-64*V-Kwd78oe30A dWk>;vmjl@#*D3(R1a9x-hqom+A9yi^4*<@3MUemi delta 30 mcmccgk@?Cy<_&T@j0T(Kd1CE0*Lj)EoBV4p-)6g4Q}_VO{S7n# diff --git a/cppcryptfs/res/ru/Resources_RU.rc b/cppcryptfs/res/ru/Resources_RU.rc index 8bd41c603415ed672603e11daaa7749ef9b5194a..f430b87af277a3ed2f03bb3006707c2ab73e18a9 100644 GIT binary patch delta 341 zcmZ2Bg{5mc%Z3==$z50YHfQ+qtTR<$fIv?M7lvSlcm`(%4+cjl8P5>J;K~rl5X#^Q zW`X2A7`z!=CL3Otu2*7+0IJJiNQ9C`3a2O!S~=sYK&yaIzG b2qUbW9Q#IcdJhvL$Kn3NM;+y_~nUQ1jyFYXI0I=B%jsO4v diff --git a/cppcryptfs/res/zh-CN/Resources_ZH-CN.rc b/cppcryptfs/res/zh-CN/Resources_ZH-CN.rc index 8ebe6dd328685f73db7bd4b77a9cdbb377ab4432..95071361a4ee0f9b07d5a9b4bd15d2f264e3fb02 100644 GIT binary patch delta 423 zcmZo#!Mto0^M+g8lc)7)Ozz21+N{E}jEkuvc=83YGR}(N+I|fNH3rAYf#$-KUy1Q; zE)ajm!EtsXZ{)FP#)`=XHe!Y-W3gV{%jh&t@Js3p*B&!IK+Si%m{y34m}mOjDSAtHlh$aVVGDY}UGj6{vCX zgD$ZCXA8@eCx1I4pzh4z!QjXMA>$c>7+e_w8A2I6!K`40c%ZB|gA0S(WXCdT_Xwc6 z42DD~X~bZ_pvw@zkk63AP|0A(U<~A&GAJ-GOmFPe578)9fawK_RTilyuxDJ4k73AT zP@4Slh^Qv2y^cV8gMrYKAq;4zGlM^aAA=i%50LLXIZ#pDO^JaxyLtWoHt_i=0DXio ZPl16a!)u1r=lkT)wx;E_6*17Kdw%8Fryq= zGB_Zn*7i@nFkNBtnHDpIfI+R?W|h_@tWed9Pj!jIOggi$OnGvfF5l+9V>9>w%(6#s diff --git a/cppcryptfs/res/zh-CN/resource.h b/cppcryptfs/res/zh-CN/resource.h index 5531b820..a468c21a 100644 --- a/cppcryptfs/res/zh-CN/resource.h +++ b/cppcryptfs/res/zh-CN/resource.h @@ -225,6 +225,8 @@ #define IDS_MEMORY_ALLOCATION_FAILURE 657 #define IDS_APLLY_LANGUAGE_RESTART 688 #define IDS_LANGUAGE_NAME 704 +#define IDS_CHACHA_REQUIRES_HKDF 705 +#define IDS_CHACHA_AESSIV_CONFLICT 706 #define IDR_LICENSE_CPPCRYPTFS 800 #define IDR_LICENSE_OPENSSL 801 #define IDR_LICENSE_RAPIDJSON 802 diff --git a/cppcryptfs/resource.h b/cppcryptfs/resource.h index affc7f81218b5a9ada6fb9c60a49e3dbbc0c61e1..98216f1e27e09c2c7ae1a92c8191f4db87e87ec7 100755 GIT binary patch delta 130 zcmZ2+hVjf<#tr`*CL4&b>3T3YGB`7U$asbz23LkahEN7iFe?}+;=$m};KJa>zzK%t zlLfUEO(CjbdK`gzf`QPJAq=SAnZcjI52)S;$aiK4f$3u~m@LRIJ=wrXWV4W?m?{9j CR2Mt| delta 18 acmX?emT|=y#tr`*CVy}g*}TnBOcelKBM7?y diff --git a/cppcryptfs/ui/CreatePropertyPage.cpp b/cppcryptfs/ui/CreatePropertyPage.cpp index 6372066a..f48a239a 100755 --- a/cppcryptfs/ui/CreatePropertyPage.cpp +++ b/cppcryptfs/ui/CreatePropertyPage.cpp @@ -45,11 +45,13 @@ THE SOFTWARE. static const WCHAR *data_encryption_types[] = { L"AES256-GCM", - L"AES256-SIV" + L"AES256-SIV", + L"XChaCha20-Poly1305" }; #define AES256_GCM_INDEX 0 #define AES256_SIV_INDEX 1 +#define AES256_CHACHA_INDEX 2 #define NUM_DATA_ENC_TYPES (sizeof(data_encryption_types)/sizeof(data_encryption_types[0])) @@ -182,6 +184,7 @@ void CCreatePropertyPage::CreateCryptfs() bool siv = false; + bool chacha = false; bool eme = false; bool plaintext = false; bool longfilenames = false; @@ -230,6 +233,8 @@ void CCreatePropertyPage::CreateCryptfs() if (cfenc == L"AES256-SIV") siv = true; + else if (cfenc == L"XChaCha20-Poly1305") + chacha = true; CString volume_name; GetDlgItemText(IDC_VOLUME_NAME, volume_name); @@ -245,7 +250,7 @@ void CCreatePropertyPage::CreateCryptfs() const int scryptN = stoi((LPCTSTR)sel); theApp.DoWaitCursor(1); - bool bResult = config.create(cpath, config_path, password.m_buf, eme, plaintext, longfilenames, siv, reverse, scryptN, volume_name, disablestreams, longnamemax, deterministicnames, error_mes); + bool bResult = config.create(cpath, config_path, password.m_buf, eme, plaintext, longfilenames, siv, chacha, reverse, scryptN, volume_name, disablestreams, longnamemax, deterministicnames, error_mes); theApp.DoWaitCursor(-1); if (!bResult) { diff --git a/cppcryptfs/ui/locutils.cpp b/cppcryptfs/ui/locutils.cpp index b272447d..0f4a74a1 100644 --- a/cppcryptfs/ui/locutils.cpp +++ b/cppcryptfs/ui/locutils.cpp @@ -1,11 +1,16 @@ #include "locutils.h" -#define VC_EXTRALEAN -#include +#include +// Load a string resource from the current module (the final EXE). +// Equivalent to MFC/ATL's CString::LoadString but without the ATL/MFC +// dependency, so it can be built on CI images that lack ATL. +// LoadStringW reads at most 255 characters per call; resources longer +// than that are truncated, but all resources here are short enough. std::wstring LocUtils::GetStringFromResources(unsigned int nID) { - ATL::CStringW str; - if (str.LoadString(nID)) { - return std::wstring((LPCWSTR)str); + wchar_t buf[256]; + int len = LoadStringW(GetModuleHandle(nullptr), nID, buf, _countof(buf)); + if (len > 0) { + return std::wstring(buf, static_cast(len)); } return L""; } diff --git a/cppcryptfsctl/cppcryptfsctl.cpp b/cppcryptfsctl/cppcryptfsctl.cpp index 56b9ad3a..a79b371a 100644 --- a/cppcryptfsctl/cppcryptfsctl.cpp +++ b/cppcryptfsctl/cppcryptfsctl.cpp @@ -153,6 +153,8 @@ static int do_self_args(int argc, wchar_t* const argv[]) bool siv = false; + bool chacha = false; + bool do_version = false; bool do_help = false; @@ -194,6 +196,7 @@ static int do_self_args(int argc, wchar_t* const argv[]) {L"streams", required_argument, 0, 'b'}, {L"volumename", required_argument, 0, 'V'}, {L"siv", no_argument, 0, 'S'}, + {L"chacha", no_argument, 0, 'X'}, {L"version", no_argument, 0, 'v' }, {L"help", no_argument, 0, 'h'}, {L"password", required_argument, 0, 'p'}, @@ -206,7 +209,7 @@ static int do_self_args(int argc, wchar_t* const argv[]) }; while (true) { - c = getopt_long(argc, argv, L"p:dI:c:sTL:b:V:Svh0:1:2:3:4:", long_options, &option_index); + c = getopt_long(argc, argv, L"p:dI:c:sTL:b:V:SXvh0:1:2:3:4:", long_options, &option_index); if (c == -1) break; @@ -276,6 +279,9 @@ static int do_self_args(int argc, wchar_t* const argv[]) case 'S': siv = true; break; + case 'X': + chacha = true; + break; case 'v': do_version = true; break; @@ -319,6 +325,11 @@ static int do_self_args(int argc, wchar_t* const argv[]) } } + if (chacha && reverse) { + wcerr << L"XChaCha20-Poly1305 cannot be used with reverse mode (reverse implies AES256-SIV)." << endl; + return 1; + } + if (do_version) { wstring prod, ver, copyright; GetProductVersionInfo(prod, ver, copyright); @@ -371,7 +382,7 @@ static int do_self_args(int argc, wchar_t* const argv[]) return 1; } - bool result = config.create(fs_path.c_str(), config_path.c_str(), password.m_buf, !plaintext_names, plaintext_names, longnames, reverse || siv, reverse, scryptn ? scryptn : DEFAULT_SCRYPTN, volume_name.c_str(), !streams, longnamemax, deterministicnames, mes); + bool result = config.create(fs_path.c_str(), config_path.c_str(), password.m_buf, !plaintext_names, plaintext_names, longnames, reverse || siv, chacha, reverse, scryptn ? scryptn : DEFAULT_SCRYPTN, volume_name.c_str(), !streams, longnamemax, deterministicnames, mes); if (!result) { wcerr << mes << endl; diff --git a/cppcryptfsctl/cppcryptfsctl.vcxproj b/cppcryptfsctl/cppcryptfsctl.vcxproj index 2794f118..55c24aaf 100644 --- a/cppcryptfsctl/cppcryptfsctl.vcxproj +++ b/cppcryptfsctl/cppcryptfsctl.vcxproj @@ -146,7 +146,7 @@ Level3 true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug false @@ -199,7 +199,7 @@ $(OutputPath)\libcppcryptfs.lib;$(OutputPath)libcommonutil.lib;$(OutputPath)libipc.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);c:\git\openssl-x86-static\lib\libcrypto.lib;Shlwapi.lib;Crypt32.lib - python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir) + python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir)" @@ -225,7 +225,7 @@ /HIGHENTROPYVA %(AdditionalOptions) - python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir) + python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir)" @@ -236,7 +236,7 @@ true true true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded Guard @@ -251,7 +251,7 @@ /HIGHENTROPYVA %(AdditionalOptions) - python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir) + python "$(ProjectDir)\..\build_scripts\make_version_header.py" "$(ProjectDir)" diff --git a/libcommonutil/libcommonutil.vcxproj b/libcommonutil/libcommonutil.vcxproj index 64a999c0..34a58d0d 100644 --- a/libcommonutil/libcommonutil.vcxproj +++ b/libcommonutil/libcommonutil.vcxproj @@ -142,7 +142,7 @@ Use Level3 true - _DEBUG;_LIB;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;_DEBUG;_LIB;%(PreprocessorDefinitions) true pch.h MultiThreadedDebug @@ -216,7 +216,7 @@ true true true - NDEBUG;_LIB;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;NDEBUG;_LIB;%(PreprocessorDefinitions) true pch.h MultiThreaded diff --git a/libcppcryptfs/config/cryptconfig.cpp b/libcppcryptfs/config/cryptconfig.cpp index ad6e4c53..b5dc90e0 100644 --- a/libcppcryptfs/config/cryptconfig.cpp +++ b/libcppcryptfs/config/cryptconfig.cpp @@ -66,7 +66,6 @@ THE SOFTWARE. #include "../libcommonutil/commonutil.h" #include "../cppcryptfs/resource.h" #include "../cppcryptfs/ui/locutils.h" -#include CryptConfig::CryptConfig() { @@ -81,6 +80,7 @@ CryptConfig::CryptConfig() m_GCMIV128 = false; m_LongNames = false; m_AESSIV = false; + m_ChaChaPoly1305 = false; m_Raw64 = false; m_HKDF = false; m_reverse = false; @@ -94,6 +94,8 @@ CryptConfig::CryptConfig() m_pGcmContentKey = NULL; + m_pChaChaContentKey = NULL; + m_fs_feature_disable_mask = 0; m_DenyAccessToOthers = false; @@ -104,6 +106,7 @@ CryptConfig::~CryptConfig() { delete m_pKeyBuf; delete m_pGcmContentKey; + delete m_pChaChaContentKey; } @@ -205,6 +208,8 @@ CryptConfig::read(wstring& mes, const WCHAR *config_file_path, bool reverse) bool bret = true; + m_warning.clear(); + try { if (!d.HasMember("EncryptedKey") || !d["EncryptedKey"].IsString()) { @@ -318,16 +323,26 @@ CryptConfig::read(wstring& mes, const WCHAR *config_file_path, bool reverse) m_Raw64 = true; } else if (!strcmp(itr->GetString(), "HKDF")) { m_HKDF = true; + } else if (!strcmp(itr->GetString(), "XChaCha20Poly1305")) { + m_ChaChaPoly1305 = true; } else { + // Unknown feature flags are tolerated (recorded as a + // warning) instead of being fatal, so that volumes using + // algorithms cppcryptfs does not implement (e.g. FIDO2) + // can still be mounted when the master key is supplied. wstring wflag; if (utf8_to_unicode(itr->GetString(), wflag)) { - CString strMsg; - strMsg.Format(LocUtils::GetStringFromResources(IDS_UNKNOWN_FEATURE_FLAG).c_str(), wflag); - mes = strMsg; + wchar_t buf[1024]; + swprintf_s(buf, _countof(buf), LocUtils::GetStringFromResources(IDS_UNKNOWN_FEATURE_FLAG).c_str(), wflag.c_str()); + wstring strMsg(buf); + if (!m_warning.empty()) + m_warning += L" "; + m_warning += strMsg; } else { - mes = LocUtils::GetStringFromResources(IDS_UNABLE_CONVERT_UNKNOWN_FLAG); + if (!m_warning.empty()) + m_warning += L" "; + m_warning += LocUtils::GetStringFromResources(IDS_UNABLE_CONVERT_UNKNOWN_FLAG); } - throw(-1); } } } @@ -464,7 +479,7 @@ bool CryptConfig::write_updated_config_file(const char *base64key, const char *s rapidjson::Value& scryptobject = d["ScryptObject"]; if (scryptobject.HasMember("N")) { rapidjson::Value salt(scryptSalt, d.GetAllocator()); - scryptobject["N"] = 1 << scryptn; + scryptobject["N"] = 1LL << scryptn; } else { throw(-1); } @@ -610,9 +625,15 @@ bool CryptConfig::check_config(wstring& mes) if (!m_EMENames && !m_PlaintextNames) mes += LocUtils::GetStringFromResources(IDS_EMENAMES_REQUIRED); - if (!m_GCMIV128) + if (!m_GCMIV128 && !m_ChaChaPoly1305) mes += LocUtils::GetStringFromResources(IDS_GCMIV128_NOT_SPECIFIED); + if (m_ChaChaPoly1305 && !m_HKDF) + mes += LocUtils::GetStringFromResources(IDS_CHACHA_REQUIRES_HKDF); + + if (m_ChaChaPoly1305 && m_AESSIV) + mes += LocUtils::GetStringFromResources(IDS_CHACHA_AESSIV_CONFLICT); + if (m_reverse && !m_AESSIV) mes += LocUtils::GetStringFromResources(IDS_REVERSE_MODE_WITHOUT_AESSIV); @@ -687,7 +708,7 @@ bool CryptConfig::decrypt_key(LPCTSTR password) throw(-1); } - int ptlen = decrypt(ciphertext, ciphertext_len, adata, adata_len, tag, m_HKDF ? pwkeyHKDF.m_buf : pwkey.m_buf, iv, m_pKeyBuf->m_buf, context.get()); + int ptlen = decrypt(ciphertext, ciphertext_len, adata, adata_len, tag, m_HKDF ? pwkeyHKDF.m_buf : pwkey.m_buf, iv, m_pKeyBuf->m_buf, context.get(), AES_MODE_GCM); if (ptlen != MASTER_KEY_LEN) throw (-1); @@ -697,6 +718,13 @@ bool CryptConfig::decrypt_key(LPCTSTR password) throw(-1); } + // Only derive the XChaCha20-Poly1305 content key when this volume uses it. + if (m_ChaChaPoly1305) { + if (!this->InitChaChaContentKey(this->GetMasterKey())) { + throw(-1); + } + } + if (m_VolumeName.size() > 0) { string vol; if (unicode_to_utf8(&m_VolumeName[0], vol)) { @@ -821,7 +849,7 @@ bool CryptConfig::encrypt_key(const wchar_t* password, const BYTE* masterkey, st memcpy(encrypted_key, iv, iv_len); int ctlen = encrypt(m_pKeyBuf->m_buf, GetMasterKeyLength(), adata, sizeof(adata), m_HKDF ? pwkeyHKDF.m_buf : pwkey.m_buf, - iv, (encrypted_key + iv_len), encrypted_key + iv_len + GetMasterKeyLength(), context.get()); + iv, (encrypted_key + iv_len), encrypted_key + iv_len + GetMasterKeyLength(), context.get(), AES_MODE_GCM); if (ctlen < 1) { error_mes = LocUtils::GetStringFromResources(IDS_UNABLE_ENCRYPT_MASTER_KEY); @@ -852,7 +880,7 @@ bool CryptConfig::encrypt_key(const wchar_t* password, const BYTE* masterkey, st } -bool CryptConfig::create(const WCHAR *path, const WCHAR *specified_config_file_path, const WCHAR *password, bool eme, bool plaintext, bool longfilenames, bool siv, bool reverse, int scryptN, const WCHAR *volume_name, bool disablestreams, int longnamemax, bool deterministicnames, wstring& error_mes) +bool CryptConfig::create(const WCHAR *path, const WCHAR *specified_config_file_path, const WCHAR *password, bool eme, bool plaintext, bool longfilenames, bool siv, bool chacha, bool reverse, int scryptN, const WCHAR *volume_name, bool disablestreams, int longnamemax, bool deterministicnames, wstring& error_mes) { if (specified_config_file_path && *specified_config_file_path == '\0') @@ -876,6 +904,14 @@ bool CryptConfig::create(const WCHAR *path, const WCHAR *specified_config_file_p if (siv) m_AESSIV = true; + if (chacha) + m_ChaChaPoly1305 = true; + + if (m_AESSIV && m_ChaChaPoly1305) { + error_mes = LocUtils::GetStringFromResources(IDS_CHACHA_AESSIV_CONFLICT); + throw(-1); + } + // Raw64 defaults to true, but must be disabled if PlaintextNames is active // (PlaintextNames implies filenames are not encrypted, so Base64 encoding is conflicting) m_Raw64 = !m_PlaintextNames; @@ -913,13 +949,13 @@ bool CryptConfig::create(const WCHAR *path, const WCHAR *specified_config_file_p if (m_reverse) { if (PathFileExists(&config_path[0])) { if (specified_config_file_path) { - CString strMsg; - strMsg.Format(LocUtils::GetStringFromResources(IDS_CONF_PATH_ALREADY_EXIST).c_str(), config_path); - error_mes = strMsg; + wchar_t buf[1024]; + swprintf_s(buf, _countof(buf), LocUtils::GetStringFromResources(IDS_CONF_PATH_ALREADY_EXIST).c_str(), config_path.c_str()); + error_mes = buf; } else { - CString strMsg; - strMsg.Format(LocUtils::GetStringFromResources(IDS_CONF_PATH_HIDDEN_EXIST).c_str(), config_path); - error_mes = strMsg; + wchar_t buf[1024]; + swprintf_s(buf, _countof(buf), LocUtils::GetStringFromResources(IDS_CONF_PATH_HIDDEN_EXIST).c_str(), config_path.c_str()); + error_mes = buf; } throw(-1); } @@ -1030,7 +1066,12 @@ bool CryptConfig::create(const WCHAR *path, const WCHAR *specified_config_file_p fprintf(fl, "\t\t\"HKDF\",\n"); if (m_Raw64) fprintf(fl, "\t\t\"Raw64\",\n"); - fprintf(fl, "\t\t\"GCMIV128\"\n"); + // XChaCha20-Poly1305 and GCMIV128 are mutually exclusive content + // encryption flags; output whichever this volume uses. + if (m_ChaChaPoly1305) + fprintf(fl, "\t\t\"XChaCha20Poly1305\"\n"); + else + fprintf(fl, "\t\t\"GCMIV128\"\n"); if (m_LongNames && m_LongNameMax != MAX_LONGNAMEMAX) { fprintf(fl, "\t],\n"); string s = "\t\"LongNameMax\": " + to_string(m_LongNameMax) + "\n"; @@ -1091,3 +1132,21 @@ bool CryptConfig::InitGCMContentKey(const BYTE *key) return true; } +bool CryptConfig::InitChaChaContentKey(const BYTE *key) +{ + if (!m_HKDF) + return true; + + m_pChaChaContentKey = new LockZeroBuffer(MASTER_KEY_LEN, false); + + if (!m_pChaChaContentKey->IsLocked()) + return false; + + m_keybuf_manager.RegisterBuf(m_pChaChaContentKey); + + if (!hkdfDerive(key, MASTER_KEY_LEN, m_pChaChaContentKey->m_buf, m_pChaChaContentKey->m_len, hkdfInfoChaChaContent)) + return false; + + return true; +} + diff --git a/libcppcryptfs/config/cryptconfig.h b/libcppcryptfs/config/cryptconfig.h index 3ed23df3..71161bef 100644 --- a/libcppcryptfs/config/cryptconfig.h +++ b/libcppcryptfs/config/cryptconfig.h @@ -34,6 +34,7 @@ THE SOFTWARE. #include "util/LockZeroBuffer.h" #include "util/KeybufManager.h" #include "util/KeyCache.h" +#include "crypt/cryptdefs.h" #define MAX_CONFIG_FILE_SIZE (16*1024*1024) // 16MB @@ -53,6 +54,7 @@ class CryptConfig private: LockZeroBuffer *m_pKeyBuf; LockZeroBuffer *m_pGcmContentKey; + LockZeroBuffer *m_pChaChaContentKey; bool m_DirIV; unsigned long long scrypt_mem(); public: @@ -62,6 +64,7 @@ class CryptConfig bool m_GCMIV128; bool m_LongNames; bool m_AESSIV; + bool m_ChaChaPoly1305; bool m_Raw64; bool m_HKDF; bool m_DenyAccessToOthers; @@ -91,13 +94,26 @@ class CryptConfig const BYTE *GetGcmContentKey() { return m_HKDF ? m_pGcmContentKey->m_buf : GetMasterKey(); }; + // Returns the content key for file data blocks (XChaCha or AES-GCM). + const BYTE *GetContentKeyForBlock() { return m_ChaChaPoly1305 ? GetChaChaContentKey() : GetGcmContentKey(); }; + + // Content encryption layout parameters. XChaCha20-Poly1305 uses a 24-byte + // IV, AES-GCM/SIV use a 16-byte IV. + int GetContentIVLen() { return m_ChaChaPoly1305 ? CHACHA_BLOCK_IV_LEN : BLOCK_IV_LEN; } + int GetCipherBlockOverhead() { return GetContentIVLen() + BLOCK_TAG_LEN; } + int GetCipherBS() { return PLAIN_BS + GetCipherBlockOverhead(); } + int GetContentMode() { return m_ChaChaPoly1305 ? AES_MODE_CHACHA : AES_MODE_GCM; } + + bool InitChaChaContentKey(const BYTE *key); + const BYTE *GetChaChaContentKey() { return m_HKDF ? m_pChaChaContentKey->m_buf : GetMasterKey(); }; + CryptConfig(); bool read(wstring& mes, const WCHAR *config_file_path = NULL, bool reverse = false); bool encrypt_key(const wchar_t* password, const BYTE *masterkey, string& base64encryptedmastekey, string& scryptSalt, wstring& error_mes); bool decrypt_key(LPCTSTR password); bool create(const WCHAR* path, const WCHAR* specified_config_path, const WCHAR* password, bool eme, bool plaintext, bool longfilenames, - bool siv, bool reverse, int scryptN, const WCHAR* volume_name, bool disablestreams, int longnamemax, bool deterministicnames, wstring& error_mes + bool siv, bool chacha, bool reverse, int scryptN, const WCHAR* volume_name, bool disablestreams, int longnamemax, bool deterministicnames, wstring& error_mes ); bool check_config(wstring& mes); @@ -108,6 +124,11 @@ class CryptConfig DWORD m_fs_feature_disable_mask; + // Warnings about non-fatal issues (e.g. an unknown feature flag that + // cppcryptfs tolerates so the volume can still be mounted). + wstring m_warning; + const wstring& GetWarning() { return m_warning; } + WCHAR get_base_drive_letter(); void CopyKeyParams(const CryptConfig& other) { diff --git a/libcppcryptfs/context/cryptcontext.cpp b/libcppcryptfs/context/cryptcontext.cpp index 2b2aa6ed..0347eaea 100644 --- a/libcppcryptfs/context/cryptcontext.cpp +++ b/libcppcryptfs/context/cryptcontext.cpp @@ -190,7 +190,8 @@ void CryptContext::GetFsInfo(FsInfo & info) info.cacheTTL = m_cache_ttl; info.caseInsensitive = IsCaseInsensitive(); info.configPath = GetConfig()->m_configPath; - info.dataEncryption = GetConfig()->m_AESSIV ? L"AES256-SIV" : L"AES256-GCM"; + info.dataEncryption = GetConfig()->m_AESSIV ? L"AES256-SIV" : + GetConfig()->m_ChaChaPoly1305 ? L"XChaCha20-Poly1305" : L"AES256-GCM"; info.fileNameEncryption = GetConfig()->m_PlaintextNames ? L"none" : L"AES256-EME"; info.multhreaded = m_multithreaded; info.ioBufferSize = m_bufferblocks * 4; diff --git a/libcppcryptfs/crypt/chacha20.cpp b/libcppcryptfs/crypt/chacha20.cpp new file mode 100644 index 00000000..da7cb66c --- /dev/null +++ b/libcppcryptfs/crypt/chacha20.cpp @@ -0,0 +1,106 @@ +/* +cppcryptfs : user-mode cryptographic virtual overlay filesystem. + +Copyright (C) 2026 fzxx (github.com/fzxx) + +cppcryptfs is based on the design of gocryptfs (github.com/rfjakob/gocryptfs) + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to deal in the above Software without the above permission notice +and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "stdafx.h" + +#include +#include + +#include "chacha20.h" + + +/* ChaCha20 quarter-round: mixes four 32-bit state words (a, b, c, d). */ +static void chacha20_quarter_round(uint32_t s[16], int a, int b, int c, int d) +{ + s[a] += s[b]; s[d] ^= s[a]; s[d] = (s[d] << 16) | (s[d] >> 16); + s[c] += s[d]; s[b] ^= s[c]; s[b] = (s[b] << 12) | (s[b] >> 20); + s[a] += s[b]; s[d] ^= s[a]; s[d] = (s[d] << 8) | (s[d] >> 24); + s[c] += s[d]; s[b] ^= s[c]; s[b] = (s[b] << 7) | (s[b] >> 25); +} + +static void wipe_words(uint32_t *p, int n) +{ + volatile uint32_t *v = p; + while (n--) + *v++ = 0; +} + +void hchacha20(const unsigned char key[32], const unsigned char nonce[16], unsigned char out[32]) +{ + /* "expand 32-byte k" constant */ + static const uint32_t constant[4] = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 + }; + + uint32_t state[16]; + + state[0] = constant[0]; + state[1] = constant[1]; + state[2] = constant[2]; + state[3] = constant[3]; + + // gocryptfs (golang.org/x/crypto/chacha20) loads the key and nonce as + // little-endian uint32 words. Read them explicitly little-endian here so + // the derived subkey matches regardless of the host's byte order. + for (int i = 0; i < 8; i++) + state[4 + i] = (uint32_t)key[4*i] | ((uint32_t)key[4*i+1] << 8) | + ((uint32_t)key[4*i+2] << 16) | ((uint32_t)key[4*i+3] << 24); + + for (int i = 0; i < 4; i++) + state[12 + i] = (uint32_t)nonce[4*i] | ((uint32_t)nonce[4*i+1] << 8) | + ((uint32_t)nonce[4*i+2] << 16) | ((uint32_t)nonce[4*i+3] << 24); + + uint32_t working[16]; + memcpy(working, state, sizeof(working)); + + for (int round = 0; round < 10; round++) { + /* column rounds */ + chacha20_quarter_round(working, 0, 4, 8, 12); + chacha20_quarter_round(working, 1, 5, 9, 13); + chacha20_quarter_round(working, 2, 6, 10, 14); + chacha20_quarter_round(working, 3, 7, 11, 15); + /* diagonal rounds */ + chacha20_quarter_round(working, 0, 5, 10, 15); + chacha20_quarter_round(working, 1, 6, 11, 12); + chacha20_quarter_round(working, 2, 7, 8, 13); + chacha20_quarter_round(working, 3, 4, 9, 14); + } + + /* HChaCha20 outputs words 0-3 and 12-15 of the permuted state directly, + * without adding the initial state (unlike ChaCha20). */ + uint32_t out_words[8]; + for (int i = 0; i < 4; i++) + out_words[i] = working[i]; + for (int i = 0; i < 4; i++) + out_words[4 + i] = working[12 + i]; + + memcpy(out, out_words, 32); + + wipe_words(state, 16); + wipe_words(working, 16); + wipe_words(out_words, 8); +} diff --git a/libcppcryptfs/crypt/chacha20.h b/libcppcryptfs/crypt/chacha20.h new file mode 100644 index 00000000..0989b127 --- /dev/null +++ b/libcppcryptfs/crypt/chacha20.h @@ -0,0 +1,35 @@ +/* +cppcryptfs : user-mode cryptographic virtual overlay filesystem. + +Copyright (C) 2026 fzxx (github.com/fzxx) + +cppcryptfs is based on the design of gocryptfs (github.com/rfjakob/gocryptfs) + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to deal in the above Software without the above permission notice +and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +#include + +/* HChaCha20 (RFC 8439 Appendix A): derive a 32-byte subkey from a 32-byte + * key and 16-byte nonce. The caller passes the first 16 bytes of the + * XChaCha20-Poly1305 24-byte nonce. */ +void hchacha20(const unsigned char key[32], const unsigned char nonce[16], unsigned char out[32]); diff --git a/libcppcryptfs/crypt/crypt.cpp b/libcppcryptfs/crypt/crypt.cpp index d905cbc4..20dafa61 100644 --- a/libcppcryptfs/crypt/crypt.cpp +++ b/libcppcryptfs/crypt/crypt.cpp @@ -34,9 +34,12 @@ THE SOFTWARE. #include "aes-siv/aes256-siv.h" #include "cryptdefs.h" #include "crypt.h" +#include "chacha20.h" #include "util/util.h" #include +#include +#include static void @@ -52,6 +55,22 @@ static void free_crypt_context(EVP_CIPHER_CTX* ctx) EVP_CIPHER_CTX_free(ctx); } +/* XChaCha20-Poly1305 uses a 32-bit block counter, so a message can span at + * most 2^38 bytes of keystream. Beyond that the counter would wrap. + * The limits differ by the 16-byte Poly1305 tag. Same as gocryptfs. */ +#define CHACHA_MAX_PLAINTEXT_LEN ((UINT64_C(1) << 38) - 64) +#define CHACHA_MAX_CIPHERTEXT_LEN ((UINT64_C(1) << 38) - 48) + +/* Derive the inner ChaCha20-Poly1305 key and 12-byte nonce from the 24-byte + * XChaCha nonce via HChaCha20, matching gocryptfs (OpenSSL backend). */ +static void derive_xchacha_inner(const unsigned char *key, const unsigned char *iv, + unsigned char derived_key[32], unsigned char inner_iv[12]) +{ + hchacha20(key, iv, derived_key); + memset(inner_iv, 0, 4); + memcpy(inner_iv + 4, iv + 16, 8); +} + shared_ptr get_crypt_context(int ivlen, int mode) { EVP_CIPHER_CTX*ctx = NULL; @@ -67,6 +86,9 @@ shared_ptr get_crypt_context(int ivlen, int mode) case AES_MODE_GCM: cipher = EVP_aes_256_gcm(); break; + case AES_MODE_CHACHA: + cipher = EVP_chacha20_poly1305(); + break; default: handleErrors(); break; @@ -81,6 +103,8 @@ shared_ptr get_crypt_context(int ivlen, int mode) if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, ivlen, NULL)) handleErrors(); } + /* XChaCha20-Poly1305 uses a 12-byte inner nonce derived via HChaCha20, + so no IV length override is needed for the inner cipher. */ } catch (int) { if (ctx) @@ -93,7 +117,7 @@ shared_ptr get_crypt_context(int ivlen, int mode) int encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *aad, int aad_len, const unsigned char *key, const unsigned char *iv, - unsigned char *ciphertext, unsigned char *tag, EVP_CIPHER_CTX* ctx) + unsigned char *ciphertext, unsigned char *tag, EVP_CIPHER_CTX* ctx, int mode) { if (!ctx) @@ -103,10 +127,23 @@ int encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *aa int ciphertext_len; - try { + /* For XChaCha20-Poly1305, derive the inner key and nonce via HChaCha20. */ + unsigned char derived_key[32]; + unsigned char inner_iv[12]; + const unsigned char *use_key = key; + const unsigned char *use_iv = iv; + + if (mode == AES_MODE_CHACHA) { + /* Refuse inputs larger than the counter limit (2^38 - 64 bytes). */ + if ((uint64_t)plaintext_len > CHACHA_MAX_PLAINTEXT_LEN) + return -1; + derive_xchacha_inner(key, iv, derived_key, inner_iv); + use_key = derived_key; + use_iv = inner_iv; + } - - if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors(); + try { + if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, use_key, use_iv)) handleErrors(); /* Provide any AAD data. This can be called zero or more times as * required @@ -129,7 +166,7 @@ int encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *aa /* Get the tag */ if (tag) { - if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) + if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag)) handleErrors(); } @@ -142,7 +179,7 @@ int encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *aa int decrypt(const unsigned char *ciphertext, int ciphertext_len, unsigned char *aad, int aad_len, unsigned char *tag, const unsigned char *key, const unsigned char *iv, - unsigned char *plaintext, EVP_CIPHER_CTX* ctx) + unsigned char *plaintext, EVP_CIPHER_CTX* ctx, int mode) { if (!ctx) @@ -150,15 +187,25 @@ int decrypt(const unsigned char *ciphertext, int ciphertext_len, unsigned char * int len; int plaintext_len; - int ret; - + /* For XChaCha20-Poly1305, derive the inner key and nonce via HChaCha20. */ + unsigned char derived_key[32]; + unsigned char inner_iv[12]; + const unsigned char *use_key = key; + const unsigned char *use_iv = iv; + + if (mode == AES_MODE_CHACHA) { + /* Refuse inputs larger than the counter limit (2^38 - 48 bytes). */ + if ((uint64_t)ciphertext_len > CHACHA_MAX_CIPHERTEXT_LEN) + return -1; + derive_xchacha_inner(key, iv, derived_key, inner_iv); + use_key = derived_key; + use_iv = inner_iv; + } try { - - /* Initialise Key and IV */ - if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors(); + if (!EVP_DecryptInit_ex(ctx, NULL, NULL, use_key, use_iv)) handleErrors(); /* Provide any AAD data. This can be called zero or more times as * required @@ -175,33 +222,24 @@ int decrypt(const unsigned char *ciphertext, int ciphertext_len, unsigned char * /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ if (tag) { - if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, tag)) handleErrors(); } /* Finalise the decryption. A positive return value indicates success, * anything else is a failure - the plaintext is not trustworthy. */ - ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len); - - } - catch (int) { - ret = -1; - } - - + if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) > 0) { + plaintext_len += len; + return plaintext_len; + } - if (ret > 0) - { - /* Success */ - plaintext_len += len; - return plaintext_len; - } - else - { - /* Verify failed */ + } catch (int) { return -1; } + + /* Verify failed */ + return -1; } int encrypt_siv(const unsigned char *plaintext, int plaintext_len, unsigned char *aad, @@ -358,7 +396,7 @@ bool encrypt_string_gcm(const wstring& str, const BYTE *key, string& base64_out) memcpy(&encrypted[0], iv, sizeof(iv)); - int ctlen = encrypt((const BYTE*)&utf8[0], (int)utf8.size(), aad, (int)sizeof(aad), key, iv, &encrypted[0] + (int)sizeof(iv), &encrypted[0] + sizeof(iv) + utf8.size(), context.get()); + int ctlen = encrypt((const BYTE*)&utf8[0], (int)utf8.size(), aad, (int)sizeof(aad), key, iv, &encrypted[0] + (int)sizeof(iv), &encrypted[0] + sizeof(iv) + utf8.size(), context.get(), AES_MODE_GCM); if (ctlen != utf8.size()) throw(-1); @@ -392,7 +430,7 @@ bool decrypt_string_gcm(const string& base64_in, const BYTE *key, wstring& str) throw(-1); vector plaintext(v.size() - BLOCK_IV_LEN - BLOCK_TAG_LEN + 1); - int ptlen = decrypt((const BYTE*)(&v[0] + BLOCK_IV_LEN), (int)v.size() - BLOCK_IV_LEN - BLOCK_TAG_LEN, adata, sizeof(adata), &v[0] + v.size() - BLOCK_TAG_LEN, key, &v[0], (BYTE*)&plaintext[0], context.get()); + int ptlen = decrypt((const BYTE*)(&v[0] + BLOCK_IV_LEN), (int)v.size() - BLOCK_IV_LEN - BLOCK_TAG_LEN, adata, sizeof(adata), &v[0] + v.size() - BLOCK_TAG_LEN, key, &v[0], (BYTE*)&plaintext[0], context.get(), AES_MODE_GCM); if (ptlen != v.size() - BLOCK_IV_LEN - BLOCK_TAG_LEN) throw(-1); @@ -412,6 +450,7 @@ bool decrypt_string_gcm(const string& base64_in, const BYTE *key, wstring& str) const char *hkdfInfoEMENames = "EME filename encryption"; const char *hkdfInfoGCMContent = "AES-GCM file content encryption"; const char *hkdfInfoSIVContent = "AES-SIV file content encryption"; +const char *hkdfInfoChaChaContent = "XChaCha20-Poly1305 file content encryption"; bool hkdfDerive(const BYTE *masterKey, int masterKeyLen, BYTE *newKey, int newKeyLen, const char *info) { @@ -432,10 +471,6 @@ bool hkdfDerive(const BYTE *masterKey, int masterKeyLen, BYTE *newKey, int newKe throw(-1); if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) throw(-1); -#if 0 - if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, "salt", 4) <= 0) - throw(-1); -#endif if (EVP_PKEY_CTX_set1_hkdf_key(pctx, masterKey, masterKeyLen) <= 0) throw(-1); if (EVP_PKEY_CTX_add1_hkdf_info(pctx, reinterpret_cast(info), (int)strlen(info)) <= 0) diff --git a/libcppcryptfs/crypt/crypt.h b/libcppcryptfs/crypt/crypt.h index ce987b2e..a661427a 100644 --- a/libcppcryptfs/crypt/crypt.h +++ b/libcppcryptfs/crypt/crypt.h @@ -41,11 +41,11 @@ shared_ptr get_crypt_context(int ivlen, int mode); int encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *aad, int aad_len, const unsigned char *key, const unsigned char *iv, - unsigned char *ciphertext, unsigned char *tag, EVP_CIPHER_CTX* ctx); + unsigned char *ciphertext, unsigned char *tag, EVP_CIPHER_CTX* ctx, int mode); int decrypt(const unsigned char *ciphertext, int ciphertext_len, unsigned char *aad, int aad_len, unsigned char *tag, const unsigned char *key, const unsigned char *iv, - unsigned char *plaintext, EVP_CIPHER_CTX* ctx); + unsigned char *plaintext, EVP_CIPHER_CTX* ctx, int mode); int encrypt_siv(const unsigned char *plaintext, int plaintext_len, unsigned char *aad, int aad_len, const unsigned char *iv, @@ -69,4 +69,5 @@ bool hkdfDerive(const BYTE *masterKey, int masterKeyLen, BYTE *newKey, int newKe extern const char *hkdfInfoEMENames; extern const char *hkdfInfoGCMContent; -extern const char *hkdfInfoSIVContent; \ No newline at end of file +extern const char *hkdfInfoSIVContent; +extern const char *hkdfInfoChaChaContent; \ No newline at end of file diff --git a/libcppcryptfs/crypt/cryptdefs.h b/libcppcryptfs/crypt/cryptdefs.h index 99de1014..426f94de 100644 --- a/libcppcryptfs/crypt/cryptdefs.h +++ b/libcppcryptfs/crypt/cryptdefs.h @@ -35,6 +35,7 @@ THE SOFTWARE. #define FILE_ID_LEN 16 #define PLAIN_BS 4096 #define BLOCK_IV_LEN 16 +#define CHACHA_BLOCK_IV_LEN 24 // XChaCha20-Poly1305 uses a 192-bit nonce #define BLOCK_SIV_LEN 16 #define ORIG_MASTER_IV_LEN 12 #define HKDF_MASTER_IV_LEN 16 @@ -46,6 +47,7 @@ THE SOFTWARE. #define MASTER_KEY_LEN 32 #define AES_MODE_GCM 1 +#define AES_MODE_CHACHA 2 #define SALT_LEN 32 diff --git a/libcppcryptfs/file/cryptfile.cpp b/libcppcryptfs/file/cryptfile.cpp index 2dff703e..ae16e471 100644 --- a/libcppcryptfs/file/cryptfile.cpp +++ b/libcppcryptfs/file/cryptfile.cpp @@ -27,6 +27,7 @@ THE SOFTWARE. */ #include "stdafx.h" +#include #include "crypt/cryptdefs.h" #include "filename/cryptfilename.h" #include "util/fileutil.h" @@ -182,7 +183,7 @@ BOOL CryptFileForward::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO shared_ptr context; GetKeys(); if (!m_con->GetConfig()->m_AESSIV) { - context = get_crypt_context(BLOCK_IV_LEN, AES_MODE_GCM); + context = get_crypt_context(m_con->GetConfig()->GetContentIVLen(), m_con->GetConfig()->GetContentMode()); if (!context) return FALSE; @@ -204,7 +205,7 @@ BOOL CryptFileForward::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO try { if (blocks_spanned > 1 && m_con->m_bufferblocks > 1) { - inputbuflen = min(m_con->m_bufferblocks, blocks_spanned)*CIPHER_BS; + inputbuflen = min(m_con->m_bufferblocks, blocks_spanned)*m_con->GetConfig()->GetCipherBS(); iobuf = IoBufferPool::getInstance().GetIoBuffer(inputbuflen, 0); if (iobuf == NULL) { SetLastError(ERROR_OUTOFMEMORY); @@ -212,7 +213,7 @@ BOOL CryptFileForward::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO } inputbuf = iobuf->m_pBuf; - long long blockoff = FILE_HEADER_LEN + (offset / PLAIN_BS)*CIPHER_BS; + long long blockoff = FILE_HEADER_LEN + (offset / PLAIN_BS)*m_con->GetConfig()->GetCipherBS(); SetOverlapped(&ov, blockoff); } @@ -229,7 +230,7 @@ BOOL CryptFileForward::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO if (inputbuf && bytesinbuf < 1) { DWORD nRead = 0; DWORD blocksleft = (DWORD)(((offset + bytesleft - 1) / PLAIN_BS) - (offset / PLAIN_BS)) + 1; - DWORD readlen = min((DWORD)inputbuflen, blocksleft*CIPHER_BS); + DWORD readlen = min((DWORD)inputbuflen, blocksleft*m_con->GetConfig()->GetCipherBS()); if (!ReadFile(m_handle, inputbuf, readlen, &nRead, &ov)) { auto LastErr = ::GetLastError(); if (LastErr != ERROR_HANDLE_EOF) @@ -307,7 +308,7 @@ BOOL CryptFileForward::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO BOOL CryptFileForward::FlushOutput(LONGLONG& beginblock, BYTE *outputbuf, int& outputbytes) { - long long outputoffset = FILE_HEADER_LEN + beginblock*CIPHER_BS; + long long outputoffset = FILE_HEADER_LEN + beginblock*m_con->GetConfig()->GetCipherBS(); GoShared(); @@ -383,14 +384,14 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw if (bWriteToEndOfFile) { LARGE_INTEGER l; l.QuadPart = m_real_file_size; - if (!adjust_file_offset_down(l)) + if (!adjust_file_offset_down(l, m_con->GetConfig()->GetCipherBlockOverhead())) return FALSE; offset = l.QuadPart; } else { if (bPagingIo) { LARGE_INTEGER l; l.QuadPart = m_real_file_size; - if (!adjust_file_offset_down(l)) + if (!adjust_file_offset_down(l, m_con->GetConfig()->GetCipherBlockOverhead())) return FALSE; if (offset >= l.QuadPart) @@ -420,7 +421,7 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw } else { LARGE_INTEGER size_down; size_down.QuadPart = m_real_file_size; - adjust_file_offset_down(size_down); + adjust_file_offset_down(size_down, m_con->GetConfig()->GetCipherBlockOverhead()); if (offset + buflen > size_down.QuadPart) { bGrowingFile = true; @@ -441,7 +442,7 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw shared_ptr context; GetKeys(); if (!m_con->GetConfig()->m_AESSIV) { - context = get_crypt_context(BLOCK_IV_LEN, AES_MODE_GCM); + context = get_crypt_context(m_con->GetConfig()->GetContentIVLen(), m_con->GetConfig()->GetContentMode()); if (!context) return FALSE; @@ -460,13 +461,13 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw BYTE ivbuf[4096]; - bool ivsonstack = static_cast(blocks_spanned) * BLOCK_IV_LEN <= sizeof(ivbuf); + bool ivsonstack = static_cast(blocks_spanned) * m_con->GetConfig()->GetContentIVLen() <= sizeof(ivbuf); try { if (blocks_spanned > 1 && m_con->m_bufferblocks > 1) { - outputbuflen = min(m_con->m_bufferblocks, blocks_spanned)*CIPHER_BS; - iobuf = IoBufferPool::getInstance().GetIoBuffer(outputbuflen, ivsonstack ? 0 : static_cast(blocks_spanned) * BLOCK_IV_LEN); + outputbuflen = min(m_con->m_bufferblocks, blocks_spanned)*m_con->GetConfig()->GetCipherBS(); + iobuf = IoBufferPool::getInstance().GetIoBuffer(outputbuflen, ivsonstack ? 0 : static_cast(blocks_spanned) * m_con->GetConfig()->GetContentIVLen()); if (iobuf == NULL) { ::SetLastError(ERROR_OUTOFMEMORY); throw(-1); @@ -480,7 +481,7 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw if (ivsonstack) { ivbufptr = ivbufbase = ivbuf; } else { - iobuf = IoBufferPool::getInstance().GetIoBuffer(0, static_cast(blocks_spanned) * BLOCK_IV_LEN); + iobuf = IoBufferPool::getInstance().GetIoBuffer(0, static_cast(blocks_spanned) * m_con->GetConfig()->GetContentIVLen()); if (iobuf == NULL) { ::SetLastError(ERROR_OUTOFMEMORY); throw(-1); @@ -489,11 +490,11 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw } } - if (!get_random_bytes(m_con, ivbufptr, blocks_spanned * BLOCK_IV_LEN)) { + if (!get_random_bytes(m_con, ivbufptr, blocks_spanned * m_con->GetConfig()->GetContentIVLen())) { throw(-1); } - BYTE cipher_buf[CIPHER_BS]; + std::vector cipher_buf(m_con->GetConfig()->GetCipherBS()); while (bytesleft > 0) { @@ -518,17 +519,17 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw beginblock = blockno; advance = write_block(m_con, outputbuf + outputbytes, INVALID_HANDLE_VALUE, m_header.fileid, blockno, p, PLAIN_BS, context.get(), ivbufptr); - ivbufptr += BLOCK_IV_LEN; + ivbufptr += m_con->GetConfig()->GetContentIVLen(); - if (advance == CIPHER_BS) { + if (advance == m_con->GetConfig()->GetCipherBS()) { advance = PLAIN_BS; } else { throw(-1); } - outputbytes += CIPHER_BS; + outputbytes += m_con->GetConfig()->GetCipherBS(); } else { - advance = write_block(m_con, cipher_buf, m_handle, m_header.fileid, blockno, p, PLAIN_BS, context.get(), ivbufptr); - ivbufptr += BLOCK_IV_LEN; + advance = write_block(m_con, cipher_buf.data(), m_handle, m_header.fileid, blockno, p, PLAIN_BS, context.get(), ivbufptr); + ivbufptr += m_con->GetConfig()->GetContentIVLen(); if (advance != PLAIN_BS) throw(-1); @@ -565,8 +566,8 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw int blockwrite = max(blockoff + blockcpy, blockbytes); - int nWritten = write_block(m_con, cipher_buf, m_handle, m_header.fileid, blockno, blockbuf, blockwrite, context.get(), ivbufptr); - ivbufptr += BLOCK_IV_LEN; + int nWritten = write_block(m_con, cipher_buf.data(), m_handle, m_header.fileid, blockno, blockbuf, blockwrite, context.get(), ivbufptr); + ivbufptr += m_con->GetConfig()->GetContentIVLen(); advance = blockcpy; @@ -600,7 +601,7 @@ BOOL CryptFileForward::Write(const unsigned char *buf, DWORD buflen, LPDWORD pNw IoBufferPool::getInstance().ReleaseIoBuffer(iobuf); // we didn't use all ivs or went past the end of our ivs which is bad - if (bRet && (ivbufptr != ivbufbase + static_cast(blocks_spanned) * BLOCK_IV_LEN)) { + if (bRet && (ivbufptr != ivbufbase + static_cast(blocks_spanned) * m_con->GetConfig()->GetContentIVLen())) { assert(false); ::SetLastError(ERROR_BAD_LENGTH); bRet = FALSE; @@ -620,9 +621,9 @@ CryptFileForward::LockFile(LONGLONG ByteOffset, LONGLONG Length) long long end_block = (ByteOffset + Length - 1) / PLAIN_BS; - long long start_offset = CIPHER_FILE_OVERHEAD + start_block*CIPHER_BS; + long long start_offset = CIPHER_FILE_OVERHEAD + start_block*m_con->GetConfig()->GetCipherBS(); - long long end_offset = CIPHER_FILE_OVERHEAD + end_block*CIPHER_BS; + long long end_offset = CIPHER_FILE_OVERHEAD + end_block*m_con->GetConfig()->GetCipherBS(); long long length = end_offset - start_offset; @@ -646,9 +647,9 @@ CryptFileForward::UnlockFile(LONGLONG ByteOffset, LONGLONG Length) long long end_block = (ByteOffset + Length - 1) / PLAIN_BS; - long long start_offset = CIPHER_FILE_OVERHEAD + start_block*CIPHER_BS; + long long start_offset = CIPHER_FILE_OVERHEAD + start_block*m_con->GetConfig()->GetCipherBS(); - long long end_offset = CIPHER_FILE_OVERHEAD + end_block*CIPHER_BS; + long long end_offset = CIPHER_FILE_OVERHEAD + end_block*m_con->GetConfig()->GetCipherBS(); long long length = end_offset - start_offset; @@ -686,7 +687,7 @@ CryptFileForward::SetEndOfFile(LONGLONG offset, BOOL bSet) size_down.QuadPart = m_real_file_size; - if (!adjust_file_offset_down(size_down)) { + if (!adjust_file_offset_down(size_down, m_con->GetConfig()->GetCipherBlockOverhead())) { return FALSE; } @@ -694,7 +695,7 @@ CryptFileForward::SetEndOfFile(LONGLONG offset, BOOL bSet) if (bSet) { up_off.QuadPart = offset; - if (!adjust_file_offset_up_truncate_zero(up_off)) + if (!adjust_file_offset_up_truncate_zero(up_off, m_con->GetConfig()->GetCipherBlockOverhead())) return FALSE; } @@ -731,7 +732,7 @@ CryptFileForward::SetEndOfFile(LONGLONG offset, BOOL bSet) shared_ptr context; GetKeys(); if (!m_con->GetConfig()->m_AESSIV) { - context = get_crypt_context(BLOCK_IV_LEN, AES_MODE_GCM); + context = get_crypt_context(m_con->GetConfig()->GetContentIVLen(), m_con->GetConfig()->GetContentMode()); if (!context) return FALSE; @@ -755,15 +756,15 @@ CryptFileForward::SetEndOfFile(LONGLONG offset, BOOL bSet) if (growing) to_write = min(to_write + nread, PLAIN_BS); - BYTE cipher_buf[CIPHER_BS]; + std::vector cipher_buf(m_con->GetConfig()->GetCipherBS()); - BYTE iv[BLOCK_IV_LEN]; + std::vector iv(m_con->GetConfig()->GetContentIVLen()); - if (!get_random_bytes(m_con, iv, BLOCK_IV_LEN)) { + if (!get_random_bytes(m_con, iv.data(), m_con->GetConfig()->GetContentIVLen())) { return FALSE; } - int nwritten = write_block(m_con, cipher_buf, m_handle, m_header.fileid, last_block, buf, to_write, context.get(), iv); + int nwritten = write_block(m_con, cipher_buf.data(), m_handle, m_header.fileid, last_block, buf, to_write, context.get(), iv.data()); if (nwritten != to_write) return FALSE; @@ -870,7 +871,7 @@ BOOL CryptFileReverse::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO shared_ptr context; GetKeys(); if (!m_con->GetConfig()->m_AESSIV) { - context = get_crypt_context(BLOCK_IV_LEN, AES_MODE_GCM); + context = get_crypt_context(m_con->GetConfig()->GetContentIVLen(), m_con->GetConfig()->GetContentMode()); if (!context) return FALSE; @@ -891,9 +892,9 @@ BOOL CryptFileReverse::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO while (bytesleft > 0) { - LONGLONG blockno = (offset - sizeof(m_header)) / CIPHER_BS; + LONGLONG blockno = (offset - sizeof(m_header)) / m_con->GetConfig()->GetCipherBS(); - int blockoff = (int)((offset - sizeof(m_header)) % CIPHER_BS); + int blockoff = (int)((offset - sizeof(m_header)) % m_con->GetConfig()->GetCipherBS()); OVERLAPPED ov; SetOverlapped(&ov, blockno * PLAIN_BS); @@ -902,7 +903,7 @@ BOOL CryptFileReverse::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO BYTE plain_buf[PLAIN_BS]; - if (blockoff == 0 && bytesleft >= CIPHER_BS) { + if (blockoff == 0 && bytesleft >= m_con->GetConfig()->GetCipherBS()) { DWORD nRead = 0; if (!ReadFile(m_handle, plain_buf, sizeof(plain_buf), &nRead, &ov)) { auto LastErr = ::GetLastError(); @@ -930,7 +931,7 @@ BOOL CryptFileReverse::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO } else { - unsigned char blockbuf[CIPHER_BS]; + std::vector blockbuf(m_con->GetConfig()->GetCipherBS()); DWORD nRead = 0; if (!ReadFile(m_handle, plain_buf, sizeof(plain_buf), &nRead, &ov)) { auto LastErr = ::GetLastError(); @@ -948,7 +949,7 @@ BOOL CryptFileReverse::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO } //int blockbytes = read_block(m_con, m_handle, m_header.fileid, blockno, blockbuf, context); - int blockbytes = write_block(m_con, blockbuf, INVALID_HANDLE_VALUE, m_header.fileid, blockno, plain_buf, (int)nRead, context.get(), m_block0iv); + int blockbytes = write_block(m_con, blockbuf.data(), INVALID_HANDLE_VALUE, m_header.fileid, blockno, plain_buf, (int)nRead, context.get(), m_block0iv); if (blockbytes < 0) throw(-1); @@ -961,7 +962,7 @@ BOOL CryptFileReverse::Read(unsigned char *buf, DWORD buflen, LPDWORD pNread, LO if (blockcpy < 1) break; - memcpy(p, blockbuf + blockoff, blockcpy); + memcpy(p, blockbuf.data() + blockoff, blockcpy); advance = blockcpy; } diff --git a/libcppcryptfs/file/cryptio.cpp b/libcppcryptfs/file/cryptio.cpp index f38af070..db2cb2f1 100644 --- a/libcppcryptfs/file/cryptio.cpp +++ b/libcppcryptfs/file/cryptio.cpp @@ -42,7 +42,17 @@ read_block(CryptContext *con, HANDLE hfile, BYTE *inputbuf, int bytesinbuf, int static_assert(BLOCK_IV_LEN == BLOCK_SIV_LEN, "BLOCK_IV_LEN != BLOCK_SIV_LEN."); static_assert(BLOCK_SIV_LEN == BLOCK_TAG_LEN, "BLOCK_SIV_LEN != BLOCK_TAG_LEN."); - long long offset = FILE_HEADER_LEN + block*CIPHER_BS; + // Content layout depends on the volume's content encryption algorithm. + // AES-GCM/SIV use a 16-byte IV, XChaCha20-Poly1305 uses a 24-byte IV. + const int ivlen = con->GetConfig()->GetContentIVLen(); + const int cipherBS = con->GetConfig()->GetCipherBS(); + const int overhead = con->GetConfig()->GetCipherBlockOverhead(); + + // Fixed-size buffer large enough for the largest possible block + // (XChaCha20-Poly1305: 24-byte IV + 4096 plaintext + 16-byte tag). + unsigned char buf[CHACHA_BLOCK_IV_LEN + PLAIN_BS + BLOCK_TAG_LEN]; + + long long offset = FILE_HEADER_LEN + block*cipherBS; OVERLAPPED ov; @@ -58,12 +68,10 @@ read_block(CryptContext *con, HANDLE hfile, BYTE *inputbuf, int bytesinbuf, int memcpy(auth_data + sizeof(be_block), fileid, FILE_ID_LEN); - unsigned char buf[CIPHER_BS]; - DWORD nread = 0; if (hfile == INVALID_HANDLE_VALUE && inputbuf) { - int to_consume = min(CIPHER_BS, bytesinbuf); + int to_consume = min(cipherBS, bytesinbuf); if (bytes_consumed != NULL) *bytes_consumed = to_consume; nread = to_consume; @@ -81,19 +89,19 @@ read_block(CryptContext *con, HANDLE hfile, BYTE *inputbuf, int bytesinbuf, int if (nread == 0) return 0; - if (nread <= CIPHER_BLOCK_OVERHEAD) { + if (nread <= (DWORD)overhead) { SetLastError(ERROR_INVALID_DATA); return -1; } int ptlen; - + if (con->GetConfig()->m_AESSIV) { ptlen = decrypt_siv((inputbuf ? inputbuf : buf) + BLOCK_IV_LEN + BLOCK_SIV_LEN, nread - BLOCK_IV_LEN - BLOCK_SIV_LEN, auth_data, sizeof(auth_data), (inputbuf ? inputbuf : buf) + BLOCK_IV_LEN, (inputbuf ? inputbuf : buf), ptbuf, &con->m_siv); } else { - ptlen = decrypt((inputbuf ? inputbuf : buf) + BLOCK_IV_LEN, nread - BLOCK_IV_LEN - BLOCK_TAG_LEN, auth_data, sizeof(auth_data), - (inputbuf ? inputbuf : buf) + nread - BLOCK_TAG_LEN, con->GetConfig()->GetGcmContentKey(), (inputbuf ? inputbuf : buf), ptbuf, openssl_crypt_context); + ptlen = decrypt((inputbuf ? inputbuf : buf) + ivlen, nread - ivlen - BLOCK_TAG_LEN, auth_data, sizeof(auth_data), + (inputbuf ? inputbuf : buf) + nread - BLOCK_TAG_LEN, con->GetConfig()->GetContentKeyForBlock(), (inputbuf ? inputbuf : buf), ptbuf, openssl_crypt_context, con->GetConfig()->GetContentMode()); } if (ptlen < 0) { // return all zeros for un-authenticated blocks (might exist if file was resized without writing) @@ -101,14 +109,14 @@ read_block(CryptContext *con, HANDLE hfile, BYTE *inputbuf, int bytesinbuf, int // if we read all zeros, then it is (probably) really from a hole in the file if (is_all_zeros(inputbuf ? inputbuf : buf, nread)) { - memset(ptbuf, 0, nread - (BLOCK_IV_LEN + BLOCK_TAG_LEN)); - return nread - (BLOCK_IV_LEN + BLOCK_TAG_LEN); + memset(ptbuf, 0, nread - overhead); + return nread - overhead; } else { // if there are any non-zero bytes, then there is definitely corrupted data, so return an error SetLastError(ERROR_DATA_CHECKSUM_ERROR); return -1; } - + } return ptlen; @@ -119,7 +127,11 @@ write_block(CryptContext *con, unsigned char *cipher_buf, HANDLE hfile, const un { - long long offset = FILE_HEADER_LEN + block*CIPHER_BS; + // Content layout depends on the volume's content encryption algorithm. + const int ivlen = con->GetConfig()->GetContentIVLen(); + const int cipherBS = con->GetConfig()->GetCipherBS(); + + long long offset = FILE_HEADER_LEN + block*cipherBS; if (!iv) return -1; @@ -141,13 +153,17 @@ write_block(CryptContext *con, unsigned char *cipher_buf, HANDLE hfile, const un unsigned char tag[BLOCK_TAG_LEN]; if (!con->GetConfig()->m_reverse) { - memcpy(cipher_buf, iv, BLOCK_IV_LEN); + memcpy(cipher_buf, iv, ivlen); } else { const unsigned char* block0iv = iv; + // Copy the whole IV first so that algorithms with a larger IV + // keep their upper bytes intact. + memcpy(cipher_buf, block0iv, ivlen); + // On a 128-bit big-endian machine, this would be the low-order 64 bits - // hence the name block0IVlow - unsigned long long block0IVlow; + // hence the name block0IVlow. Only the first 16 bytes are modified. + unsigned long long block0IVlow; static_assert(BLOCK_SIV_LEN == 16, "BLOCK_SIV_LEN != 16."); static_assert(sizeof(block0IVlow) == 8, "sizeof(block0IVlow) != 8."); @@ -159,10 +175,7 @@ write_block(CryptContext *con, unsigned char *cipher_buf, HANDLE hfile, const un block0IVlow = MakeBigEndian(block0IVlow); - memcpy(cipher_buf, block0iv, 8); memcpy(cipher_buf + 8, &block0IVlow, sizeof(block0IVlow)); - - } bool siv = con->GetConfig()->m_AESSIV; @@ -173,30 +186,30 @@ write_block(CryptContext *con, unsigned char *cipher_buf, HANDLE hfile, const un ctlen = encrypt_siv(ptbuf, ptlen, auth_data, sizeof(auth_data), cipher_buf, cipher_buf + BLOCK_IV_LEN + BLOCK_SIV_LEN, cipher_buf + BLOCK_IV_LEN, &con->m_siv); } else { - ctlen = encrypt(ptbuf, ptlen, auth_data, sizeof(auth_data), con->GetConfig()->GetGcmContentKey(), - cipher_buf, cipher_buf + BLOCK_IV_LEN, tag, openssl_crypt_context); + ctlen = encrypt(ptbuf, ptlen, auth_data, sizeof(auth_data), con->GetConfig()->GetContentKeyForBlock(), + cipher_buf, cipher_buf + ivlen, tag, openssl_crypt_context, con->GetConfig()->GetContentMode()); } if (ctlen < 0 || ctlen > PLAIN_BS) return -1; if (!siv) - memcpy(cipher_buf + BLOCK_IV_LEN + ctlen, tag, sizeof(tag)); + memcpy(cipher_buf + ivlen + ctlen, tag, sizeof(tag)); if (!con->GetConfig()->m_reverse && hfile != INVALID_HANDLE_VALUE) { DWORD nWritten = 0; - if (!WriteFile(hfile, cipher_buf, BLOCK_IV_LEN + ctlen + sizeof(tag), &nWritten, &ov)) { + if (!WriteFile(hfile, cipher_buf, ivlen + ctlen + sizeof(tag), &nWritten, &ov)) { return -1; } - - if (nWritten == BLOCK_IV_LEN + ctlen + sizeof(tag)) { + + if (nWritten == ivlen + ctlen + sizeof(tag)) { return ptlen; } else { return -1; } } else { - return BLOCK_IV_LEN + ctlen + sizeof(tag); + return ivlen + ctlen + sizeof(tag); } } diff --git a/libcppcryptfs/file/iobufferpool.h b/libcppcryptfs/file/iobufferpool.h index 3242a732..8063f146 100644 --- a/libcppcryptfs/file/iobufferpool.h +++ b/libcppcryptfs/file/iobufferpool.h @@ -68,8 +68,10 @@ class IoBufferPool { public: - // the size below is to accomodate the maximum i/o buffer size + enough IVs to write up to 64MB - static const size_t m_max_pool_buffer_size = ((MAX_IO_BUFFER_KB * 1024) / PLAIN_BS) * CIPHER_BS + ((64 * 1024 * 1024) / PLAIN_BS) * BLOCK_IV_LEN; + // the size below is to accomodate the maximum i/o buffer size + enough IVs to write up to 64MB. + // Use the largest possible IV length (XChaCha20-Poly1305's 24-byte nonce) so the pool + // is large enough for every supported content-encryption algorithm. + static const size_t m_max_pool_buffer_size = ((MAX_IO_BUFFER_KB * 1024) / PLAIN_BS) * CIPHER_BS + ((64 * 1024 * 1024) / PLAIN_BS) * CHACHA_BLOCK_IV_LEN; static IoBufferPool& getInstance(); diff --git a/libcppcryptfs/filename/cryptfilename.cpp b/libcppcryptfs/filename/cryptfilename.cpp index 97daa6ab..bcb9b69b 100644 --- a/libcppcryptfs/filename/cryptfilename.cpp +++ b/libcppcryptfs/filename/cryptfilename.cpp @@ -84,7 +84,7 @@ derive_path_iv(CryptContext *con, const WCHAR *path, unsigned char *iv, const ch wpath = pathstr; if (wpath.length() > 0) { - if (wpath[wpath.length() - 1] == '\\' || wpath[wpath.length()] - 1 == '/') + if (wpath[wpath.length() - 1] == '\\' || wpath[wpath.length() - 1] == '/') wpath.erase(wpath.length() - 1); } diff --git a/libcppcryptfs/libcppcryptfs.vcxproj b/libcppcryptfs/libcppcryptfs.vcxproj index 0ae2a1fb..1f4729b9 100644 --- a/libcppcryptfs/libcppcryptfs.vcxproj +++ b/libcppcryptfs/libcppcryptfs.vcxproj @@ -40,7 +40,7 @@ v145 Unicode Spectre - + StaticLibrary false @@ -48,21 +48,21 @@ true Unicode Spectre - + StaticLibrary true v145 Unicode Spectre - + StaticLibrary true v145 Unicode Spectre - + StaticLibrary false @@ -70,7 +70,7 @@ true Unicode Spectre - + StaticLibrary false @@ -78,7 +78,7 @@ true Unicode Spectre - + @@ -186,7 +186,7 @@ Level3 Disabled true - _DEBUG;_LIB;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;_DEBUG;_LIB;%(PreprocessorDefinitions) true $(ProjectDir);c:\git\rapidjson\include;C:\Program Files\OpenSSL\include MultiThreadedDebug @@ -229,7 +229,7 @@ true true true - NDEBUG;_LIB;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;NDEBUG;_LIB;%(PreprocessorDefinitions) true /Qspectre %(AdditionalOptions) $(ProjectDir);c:\git\rapidjson\include;C:\Program Files\OpenSSL\include @@ -254,6 +254,7 @@ + @@ -295,6 +296,7 @@ + diff --git a/libcppcryptfs/libcppcryptfs.vcxproj.filters b/libcppcryptfs/libcppcryptfs.vcxproj.filters index f7036600..41d68215 100644 --- a/libcppcryptfs/libcppcryptfs.vcxproj.filters +++ b/libcppcryptfs/libcppcryptfs.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/libcppcryptfs/util/fileutil.cpp b/libcppcryptfs/util/fileutil.cpp index ffc87080..e04a93a6 100644 --- a/libcppcryptfs/util/fileutil.cpp +++ b/libcppcryptfs/util/fileutil.cpp @@ -67,7 +67,7 @@ VirtualAttributesNameFile(DWORD attr) #endif // _WIN32 bool -adjust_file_offset_down(LARGE_INTEGER& l) +adjust_file_offset_down(LARGE_INTEGER& l, int overhead) { long long offset = l.QuadPart; @@ -77,8 +77,10 @@ adjust_file_offset_down(LARGE_INTEGER& l) if (offset == 0) return true; - long long blocks = (offset - CIPHER_FILE_OVERHEAD + CIPHER_BS - 1) / CIPHER_BS; - offset -= (blocks*CIPHER_BLOCK_OVERHEAD + CIPHER_FILE_OVERHEAD); + int cipherBS = PLAIN_BS + overhead; + + long long blocks = (offset - CIPHER_FILE_OVERHEAD + cipherBS - 1) / cipherBS; + offset -= (blocks*overhead + CIPHER_FILE_OVERHEAD); if (offset < 0) return false; @@ -88,7 +90,7 @@ adjust_file_offset_down(LARGE_INTEGER& l) } bool -adjust_file_offset_up(LARGE_INTEGER& l) +adjust_file_offset_up(LARGE_INTEGER& l, int overhead) { long long offset = l.QuadPart; @@ -99,25 +101,25 @@ adjust_file_offset_up(LARGE_INTEGER& l) return true; long long blocks = (offset + PLAIN_BS - 1) / PLAIN_BS; - offset += (blocks*CIPHER_BLOCK_OVERHEAD + CIPHER_FILE_OVERHEAD); + offset += (blocks*overhead + CIPHER_FILE_OVERHEAD); l.QuadPart = offset; return true; } -bool adjust_file_size_down(LARGE_INTEGER& l) +bool adjust_file_size_down(LARGE_INTEGER& l, int overhead) { - return adjust_file_offset_down(l); + return adjust_file_offset_down(l, overhead); } -bool adjust_file_size_up(LARGE_INTEGER& l) +bool adjust_file_size_up(LARGE_INTEGER& l, int overhead) { - return adjust_file_offset_up(l); + return adjust_file_offset_up(l, overhead); } bool -adjust_file_offset_up_truncate_zero(LARGE_INTEGER& l) +adjust_file_offset_up_truncate_zero(LARGE_INTEGER& l, int overhead) { long long offset = l.QuadPart; @@ -128,7 +130,7 @@ adjust_file_offset_up_truncate_zero(LARGE_INTEGER& l) return true; long long blocks = (offset + PLAIN_BS - 1) / PLAIN_BS; - offset += blocks*CIPHER_BLOCK_OVERHEAD + CIPHER_FILE_OVERHEAD; + offset += blocks*overhead + CIPHER_FILE_OVERHEAD; l.QuadPart = offset; @@ -243,10 +245,10 @@ convert_fdata(CryptContext *con, BOOL isRoot, const BYTE *dir_iv, const WCHAR *p l.LowPart = fdata.nFileSizeLow; l.HighPart = fdata.nFileSizeHigh; if (con->GetConfig()->m_reverse) { - if (!adjust_file_size_up(l)) + if (!adjust_file_size_up(l, con->GetConfig()->GetCipherBlockOverhead())) return false; } else { - if (!adjust_file_size_down(l)) + if (!adjust_file_size_down(l, con->GetConfig()->GetCipherBlockOverhead())) return false; } fdata.nFileSizeHigh = l.HighPart; @@ -589,10 +591,10 @@ get_file_information(CryptContext *con, function getEncPath, LPCWSTR l.HighPart = pInfo->nFileSizeHigh; if (con->GetConfig()->m_reverse) { - if (!adjust_file_size_up(l)) + if (!adjust_file_size_up(l, con->GetConfig()->GetCipherBlockOverhead())) throw((int)ERROR_INVALID_PARAMETER); } else { - if (!adjust_file_size_down(l)) + if (!adjust_file_size_down(l, con->GetConfig()->GetCipherBlockOverhead())) throw((int)ERROR_INVALID_PARAMETER); } @@ -1018,10 +1020,10 @@ convert_find_stream_data(CryptContext *con, LPCWSTR pt_path, LPCWSTR path, WIN32 } if (reverse) { - if (!adjust_file_size_up(fdata.StreamSize)) + if (!adjust_file_size_up(fdata.StreamSize, con->GetConfig()->GetCipherBlockOverhead())) return false; } else { - if (!adjust_file_size_down(fdata.StreamSize)) + if (!adjust_file_size_down(fdata.StreamSize, con->GetConfig()->GetCipherBlockOverhead())) return false; } diff --git a/libcppcryptfs/util/fileutil.h b/libcppcryptfs/util/fileutil.h index 5224dfec..4c7c0354 100644 --- a/libcppcryptfs/util/fileutil.h +++ b/libcppcryptfs/util/fileutil.h @@ -53,20 +53,23 @@ get_file_information(CryptContext *con, function getFileName, LPCWSTR bool create_dir_iv(CryptContext *con, LPCWSTR path); // path is unencrypted +// overhead is the per-block cipher overhead (IV len + tag len). It must match +// the volume's content encryption algorithm: 32 for AES-GCM/SIV, 40 for +// XChaCha20-Poly1305. Pass CryptConfig::GetCipherBlockOverhead() at the call site. bool -adjust_file_offset_down(LARGE_INTEGER& l); +adjust_file_offset_down(LARGE_INTEGER& l, int overhead); bool -adjust_file_offset_up(LARGE_INTEGER& l); +adjust_file_offset_up(LARGE_INTEGER& l, int overhead); bool -adjust_file_size_down(LARGE_INTEGER& l); +adjust_file_size_down(LARGE_INTEGER& l, int overhead); bool -adjust_file_size_up(LARGE_INTEGER& l); +adjust_file_size_up(LARGE_INTEGER& l, int overhead); bool -adjust_file_offset_up_truncate_zero(LARGE_INTEGER& l); +adjust_file_offset_up_truncate_zero(LARGE_INTEGER& l, int overhead); bool is_empty_directory(LPCWSTR path, BOOL bMustReallyBeEmpty = FALSE, CryptContext *con = nullptr); diff --git a/libcppcryptfs/util/util.cpp b/libcppcryptfs/util/util.cpp index aa05290e..7341e8ef 100644 --- a/libcppcryptfs/util/util.cpp +++ b/libcppcryptfs/util/util.cpp @@ -43,7 +43,6 @@ THE SOFTWARE. #ifdef _WIN32 #include #include -#include #include #endif @@ -313,13 +312,21 @@ base64_encode(const BYTE *data, DWORD datalen, string& storage, bool urlTransfor if (base64str = base64.get()) { - // ATL Base64Encode() is supposedly way faster than CryptBinaryToString() - - bResult = Base64Encode(data, (int)datalen, base64str, &base64len, ATL_BASE64_FLAG_NOCRLF); - - // ATL Base64Encode() doesn't null terminate the string - if (bResult) - base64str[base64len] = '\0'; + // CryptBinaryToStringA is a Win32 API (see wincrypt.h) that does + // not depend on ATL. Encoding with CRYPT_STRING_BASE64 then stripping + // its CR/LF line breaks is equivalent to ATL's ATL_BASE64_FLAG_NOCRLF + // (single line, no line breaks). + DWORD outlen = (DWORD)base64len; + bResult = CryptBinaryToStringA(data, datalen, CRYPT_STRING_BASE64, base64str, &outlen); + if (bResult) { + char *dst = base64str; + for (DWORD i = 0; i < outlen; i++) { + if (base64str[i] != '\r' && base64str[i] != '\n') + *dst++ = base64str[i]; + } + *dst = '\0'; + outlen = (DWORD)(dst - base64str); + } } else { bResult = FALSE; @@ -733,6 +740,7 @@ Initializing (cppcryptfsctl only): -T, --plaintext use plaintext filenames (default is AES256-EME) --deterministicnames disable creation of gocryptfs.diriv files. -S, --siv use AES256-SIV for data encr (default is GCM) + -X, --chacha use XChaCha20-Poly1305 for data encr (default is GCM) -L, --longnames [1|0] enable/disable LFNs. defaults to enabled (1) -b, --streams [1|0] enable/disable streams. defaults to enabled (1) --longnamemax N limit filenames to at most N characters diff --git a/libcppcryptfs/util/wintypes.h b/libcppcryptfs/util/wintypes.h index 6efb2cfe..63c9476e 100644 --- a/libcppcryptfs/util/wintypes.h +++ b/libcppcryptfs/util/wintypes.h @@ -1,52 +1,52 @@ #pragma once -// -// types.h -// cppcryptfs -// -// Created by Bailey Brown on 12/8/16. -// Copyright 2016-2026 Bailey Brown. All rights reserved. -// - -#ifndef wintypes_h -#define wintypes_h - -#ifdef _WIN32 -#include "stdafx.h" -#else - -#define MAX_PATH 260 - -typedef wchar_t WCHAR; -typedef const WCHAR *LPCWSTR; - -typedef unsigned char BYTE; -typedef BYTE *LPBYTE; - -typedef unsigned int DWORD, *LPDWORD; -typedef int LONG; -typedef long long LONGLONG; - -typedef union _LARGE_INTEGER { - struct { - DWORD LowPart; - LONG HighPart; - }; - struct { - DWORD LowPart; - LONG HighPart; - } u; - LONGLONG QuadPart; -} LARGE_INTEGER, *PLARGE_INTEGER; - -typedef struct _WIN32_FIND_DATAW { - WCHAR cFileName[MAX_PATH]; -} WIN32_FIND_DATAW; - -typedef WIN32_FIND_DATAW WIN32_FIND_DATA; - -typedef int BOOL; -typedef unsigned char byte; - -#endif // _WIN32 -#endif /* wintypes_h */ +// +// types.h +// cppcryptfs +// +// Created by Bailey Brown on 12/8/16. +// Copyright 2016-2026 Bailey Brown. All rights reserved. +// + +#ifndef wintypes_h +#define wintypes_h + +#ifdef _WIN32 +#include "stdafx.h" +#else + +#define MAX_PATH 260 + +typedef wchar_t WCHAR; +typedef const WCHAR *LPCWSTR; + +typedef unsigned char BYTE; +typedef BYTE *LPBYTE; + +typedef unsigned int DWORD, *LPDWORD; +typedef int LONG; +typedef long long LONGLONG; + +typedef union _LARGE_INTEGER { + struct { + DWORD LowPart; + LONG HighPart; + }; + struct { + DWORD LowPart; + LONG HighPart; + } u; + LONGLONG QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; + +typedef struct _WIN32_FIND_DATAW { + WCHAR cFileName[MAX_PATH]; +} WIN32_FIND_DATAW; + +typedef WIN32_FIND_DATAW WIN32_FIND_DATA; + +typedef int BOOL; +typedef unsigned char byte; + +#endif // _WIN32 +#endif /* wintypes_h */ diff --git a/libipc/libipc.vcxproj b/libipc/libipc.vcxproj index 506f3209..a64bfd02 100644 --- a/libipc/libipc.vcxproj +++ b/libipc/libipc.vcxproj @@ -142,7 +142,7 @@ Use Level3 true - _DEBUG;_LIB;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;_DEBUG;_LIB;%(PreprocessorDefinitions) true pch.h MultiThreadedDebug @@ -216,7 +216,7 @@ true true true - NDEBUG;_LIB;%(PreprocessorDefinitions) + RAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN;NDEBUG;_LIB;%(PreprocessorDefinitions) true pch.h MultiThreaded diff --git a/version.txt b/version.txt index 58800642..6aa293c1 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.4.4.10 +1.4.4.11