Skip to content

Commit

Permalink
Parse/store long name config
Browse files Browse the repository at this point in the history
Separate the lookup table in another file
  • Loading branch information
netheril96 committed Feb 27, 2024
1 parent f9dcbbb commit 1292c0d
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 205 deletions.
65 changes: 31 additions & 34 deletions sources/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,25 +304,21 @@ void hmac_sha256(const securefs::key_type& base_key,
hmac.TruncatedFinal(out_key.data(), out_key.size());
}

Json::Value generate_config(unsigned int version,
const std::string& pbkdf_algorithm,
const CryptoPP::AlignedSecByteBlock& master_key,
Json::Value generate_config(const std::string& pbkdf_algorithm,
StringRef maybe_key_file_path,
const securefs::key_type& salt,
const void* password,
size_t pass_len,
unsigned block_size,
unsigned iv_size,
unsigned max_padding,
unsigned rounds)
unsigned rounds,
const FSConfig& fsconfig)
{
securefs::key_type effective_salt;
hmac_sha256(salt, maybe_key_file_path, effective_salt);

Json::Value config;
config["version"] = version;
config["version"] = fsconfig.version;
key_type password_derived_key;
CryptoPP::AlignedSecByteBlock encrypted_master_key(nullptr, master_key.size());
CryptoPP::AlignedSecByteBlock encrypted_master_key(nullptr, fsconfig.master_key.size());

if (pbkdf_algorithm == PBKDF_ALGO_PKCS5)
{
Expand Down Expand Up @@ -390,15 +386,16 @@ Json::Value generate_config(unsigned int version,
CryptoPP::GCM<CryptoPP::AES>::Encryption encryptor;
encryptor.SetKeyWithIV(
password_derived_key.data(), password_derived_key.size(), iv, array_length(iv));
encryptor.EncryptAndAuthenticate(encrypted_master_key.data(),
mac,
array_length(mac),
iv,
array_length(iv),
reinterpret_cast<const byte*>(get_version_header(version)),
strlen(get_version_header(version)),
master_key.data(),
master_key.size());
encryptor.EncryptAndAuthenticate(
encrypted_master_key.data(),
mac,
array_length(mac),
iv,
array_length(iv),
reinterpret_cast<const byte*>(get_version_header(fsconfig.version)),
strlen(get_version_header(fsconfig.version)),
fsconfig.master_key.data(),
fsconfig.master_key.size());

Json::Value encrypted_key;
encrypted_key["IV"] = hexify(iv, array_length(iv));
Expand All @@ -407,14 +404,18 @@ Json::Value generate_config(unsigned int version,

config["encrypted_key"] = std::move(encrypted_key);

if (version >= 2)
if (fsconfig.version >= 2)
{
config["block_size"] = block_size;
config["iv_size"] = iv_size;
config["block_size"] = fsconfig.block_size;
config["iv_size"] = fsconfig.iv_size;
}
if (max_padding > 0)
if (fsconfig.max_padding > 0)
{
config["max_padding"] = max_padding;
config["max_padding"] = fsconfig.max_padding;
}
if (fsconfig.long_name_component)
{
config["long_name_component"] = true;
}
return config;
}
Expand Down Expand Up @@ -489,6 +490,7 @@ FSConfig parse_config(const Json::Value& config,

fsconfig.version = config["version"].asUInt();
fsconfig.max_padding = config.get("max_padding", 0u).asUInt();
fsconfig.long_name_component = config.get("long_name_component", false).asBool();

if (fsconfig.version == 1)
{
Expand Down Expand Up @@ -625,17 +627,8 @@ void CommandBase::write_config(FileStream* stream,
{
key_type salt;
generate_random(salt.data(), salt.size());
auto str = generate_config(config.version,
pbdkf_algorithm,
config.master_key,
maybe_key_file_path,
salt,
password,
pass_len,
config.block_size,
config.iv_size,
config.max_padding,
rounds)
auto str = generate_config(
pbdkf_algorithm, maybe_key_file_path, salt, password, pass_len, rounds, config)
.toStyledString();
stream->sequential_write(str.data(), str.size());
}
Expand Down Expand Up @@ -1367,6 +1360,10 @@ class MountCommand : public _SinglePasswordCommandBase
}
fsopt.flags.value() |= kOptionNoNameTranslation;
}
if (config.long_name_component)
{
fsopt.flags.value() |= kOptionLongNameComponent;
}

if (config.version < 4)
{
Expand Down
1 change: 1 addition & 0 deletions sources/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct FSConfig
unsigned iv_size{};
unsigned version{};
unsigned max_padding = 0;
bool long_name_component = true;
};

class CommandBase
Expand Down
2 changes: 1 addition & 1 deletion sources/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace securefs
{
const unsigned kOptionNoAuthentication = 0x1, kOptionReadOnly = 0x2, kOptionStoreTime = 0x4,
kOptionCaseFoldFileName = 0x8, kOptionNFCFileName = 0x10, kOptionSkipDotDot = 0x20,
kOptionNoNameTranslation = 0x40;
kOptionNoNameTranslation = 0x40, kOptionLongNameComponent = 0x80;
} // namespace securefs
140 changes: 9 additions & 131 deletions sources/lite_fs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
#include "lock_guard.h"
#include "logger.h"

#include <absl/strings/escaping.h>
#include <absl/strings/str_cat.h>
#include <cryptopp/base32.h>

#include <cerrno>
Expand Down Expand Up @@ -49,25 +47,6 @@ namespace lite
{
m_name_encryptor = std::make_shared<AES_SIV>(name_key.data(), name_key.size());
}
auto db_path = root->norm_path(".long_names.db");
try
{
m_name_lookup_ = std::make_shared<LongNameLookupTable>(
#ifdef _WIN32
narrow_string(db_path)
#else
db_path
#endif
,
flags & kOptionReadOnly);
}
catch (const std::exception& e)
{
ERROR_LOG("Failed to open database for long name lookup, so long filenames will not be "
"supported: %s",
e.what());
m_name_lookup_ = {};
}
}

FileSystem::~FileSystem() {}
Expand All @@ -78,25 +57,9 @@ namespace lite
return strprintf("Invalid filename \"%s\"", m_filename.c_str());
}

// Longer file names would be encrypted and base32 to > 255 bytes, and that exceeds most
// filesystem's limits.
constexpr size_t max_normal_filename_component_size = 143;

static std::string encrypt_long_name_component(AES_SIV& encryptor, absl::string_view long_name)
{
unsigned char sha256[32];
CryptoPP::SHA256 calc;
calc.Update(reinterpret_cast<const byte*>(long_name.data()), long_name.size());
calc.TruncatedFinal(sha256, sizeof(sha256));
std::vector<unsigned char> buffer(sizeof(sha256) + AES_SIV::IV_SIZE);
encryptor.encrypt_and_authenticate(
sha256, sizeof(sha256), nullptr, 0, buffer.data() + AES_SIV::IV_SIZE, buffer.data());
return absl::StrCat("_", hexify(buffer), "_");
}

std::string encrypt_path(AES_SIV& encryptor, StringRef path, LongNameLookupTable* maybe_table)
std::string encrypt_path(AES_SIV& encryptor, StringRef path)
{
byte buffer[max_normal_filename_component_size * 2 + 32];
byte buffer[2032];
std::string result;
result.reserve((path.size() * 8 + 4) / 5);
size_t last_nonseparator_index = 0;
Expand All @@ -110,16 +73,12 @@ namespace lite
{
const char* slice = path.data() + last_nonseparator_index;
size_t slice_size = i - last_nonseparator_index;
if (slice_size <= max_normal_filename_component_size)
{
encryptor.encrypt_and_authenticate(
slice, slice_size, nullptr, 0, buffer + AES_SIV::IV_SIZE, buffer);
base32_encode(buffer, slice_size + AES_SIV::IV_SIZE, encoded_part);
result.append(encoded_part);
}
else
{
}
if (slice_size > 2000)
throwVFSException(ENAMETOOLONG);
encryptor.encrypt_and_authenticate(
slice, slice_size, nullptr, 0, buffer + AES_SIV::IV_SIZE, buffer);
base32_encode(buffer, slice_size + AES_SIV::IV_SIZE, encoded_part);
result.append(encoded_part);
}
if (i < path.size())
result.push_back('/');
Expand All @@ -129,7 +88,7 @@ namespace lite
return result;
}

std::string decrypt_path(AES_SIV& decryptor, StringRef path, LongNameLookupTable* maybe_table)
std::string decrypt_path(AES_SIV& decryptor, StringRef path)
{
byte string_buffer[2032];
std::string result, decoded_part;
Expand Down Expand Up @@ -500,87 +459,6 @@ namespace lite

Base::~Base() {}

LongNameLookupTable::LongNameLookupTable(StringRef filename, bool readonly)
{
db_ = SQLiteDB(
filename.c_str(),
SQLITE_OPEN_NOMUTEX
| (readonly ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)),
nullptr);
if (!readonly)
{
db_.exec(R"(
create table if not exists encrypted_mappings (
encrypted_hash blob not null primary key,
encrypted_name blob not null
);
)");
}
}

LongNameLookupTable::~LongNameLookupTable() {}

std::vector<unsigned char>
LongNameLookupTable::lookup(absl::Span<const unsigned char> encrypted_hash)
{
SQLiteStatement q(
db_, "select encrypted_name from encrypted_mappings where encrypted_hash = ?;");
q.reset();
q.bind_blob(1, encrypted_hash);
if (!q.step())
{
return {};
}
auto span = q.get_blob(0);
return std::vector<unsigned char>(span.begin(), span.end());
}

void LongNameLookupTable::insert_or_update(absl::Span<const unsigned char> encrypted_hash,
absl::Span<const unsigned char> encrypted_long_name)
{
SQLiteStatement q(db_, R"(
insert or ignore into encrypted_mappings
(encrypted_hash, encrypted_name)
values (?, ?);
)");
q.reset();
q.bind_blob(1, encrypted_hash);
q.bind_blob(2, encrypted_long_name);
q.step();
}

void LongNameLookupTable::delete_once(absl::Span<const unsigned char> encrypted_hash)
{
SQLiteStatement q(db_, R"(
delete from encrypted_mappings
where encrypted_hash = ?;
)");
q.reset();
q.bind_blob(1, encrypted_hash);
q.step();
}

void LongNameLookupTable::begin() { db_.exec("begin;"); }

void LongNameLookupTable::finish() noexcept
{
try
{
if (std::uncaught_exception())
{
db_.exec("rollback");
}
else
{
db_.exec("commit");
}
}
catch (const std::exception& e)
{
ERROR_LOG("Failed to commit or rollback: %s", e.what());
}
}

#ifdef __APPLE__
ssize_t
FileSystem::getxattr(const char* path, const char* name, void* buf, size_t size) noexcept
Expand Down
41 changes: 2 additions & 39 deletions sources/lite_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "mystring.h"
#include "myutils.h"
#include "platform.h"
#include "sqlite_helper.h"

#include <absl/base/thread_annotations.h>
#include <absl/types/span.h>
Expand Down Expand Up @@ -130,43 +129,8 @@ namespace lite

typedef std::unique_ptr<File> AutoClosedFile;

class LongNameLookupTable
{
public:
LongNameLookupTable(StringRef filename, bool readonly);
~LongNameLookupTable();

std::vector<unsigned char> lookup(absl::Span<const unsigned char> encrypted_hash)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
void insert_or_update(absl::Span<const unsigned char> encrypted_hash,
absl::Span<const unsigned char> encrypted_long_name)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
void delete_once(absl::Span<const unsigned char> encrypted_hash)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);

template <typename Callback>
auto transact(Callback&& callback) -> decltype(callback(this))
{
LockGuard<Mutex> lg(mu_);
begin();
auto guard = stdex::make_guard([this]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_)
{ this->finish(); });
return callback(this);
}

private:
Mutex mu_;
SQLiteDB db_ ABSL_GUARDED_BY(mu_);

private:
void begin() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
void finish() noexcept ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
};

std::string
encrypt_path(AES_SIV& encryptor, StringRef path, LongNameLookupTable* maybe_table = nullptr);
std::string
decrypt_path(AES_SIV& decryptor, StringRef path, LongNameLookupTable* maybe_table = nullptr);
std::string encrypt_path(AES_SIV& encryptor, StringRef path);
std::string decrypt_path(AES_SIV& decryptor, StringRef path);

class InvalidFilenameException : public VerificationException
{
Expand All @@ -187,7 +151,6 @@ namespace lite

private:
std::shared_ptr<AES_SIV> m_name_encryptor;
std::shared_ptr<LongNameLookupTable> m_name_lookup_;
key_type m_content_key;
CryptoPP::GCM<CryptoPP::AES>::Encryption m_xattr_enc;
CryptoPP::GCM<CryptoPP::AES>::Decryption m_xattr_dec;
Expand Down
1 change: 1 addition & 0 deletions sources/lite_long_name_lookup_table.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "lite_long_name_lookup_table.h"

0 comments on commit 1292c0d

Please sign in to comment.