Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add encryption support #102

Merged
merged 3 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@ X.Y.Z Release notes (YYYY-MM-DD)
* Using a property type of vector of enums would cause a compilation error (since 0.1.0).

### Enhancements
* None
* The Sync metadata Realm is now encrypted by default on Apple platforms unless the `REALM_DISABLE_METADATA_ENCRYPTION` environment variable is set.
To enable encryption on the metadata Realm on other platforms you must set an encryption key on `realm::App::configuration`.
```cpp
std::array<char, 64> example_key = {...};
realm::App::configuration app_config;
app_config.app_id = ...
app_config.metadata_encryption_key = example_key;
auto encrypted_app = realm::App(app_config);
```
* Add ability to encrypt a Realm. Usage: `realm::config::set_encryption_key(const std::array<char, 64>&)`.

### Breaking Changes
* None
* `realm::App(const std::string &app_id, const std::optional<std::string> &base_url,
const std::optional<std::string> &path, const std::optional<std::map<std::string, std::string>> &custom_http_headers)` has been deprecated.
use `realm::App(const realm::App::configuration&);` instead.

### Compatibility
* Fileformat: Generates files with format v22.
Expand Down
51 changes: 34 additions & 17 deletions src/cpprealm/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,40 +343,45 @@ namespace realm {
return credentials(app::AppCredentials::function(payload));
}

App::App(const std::string &app_id,
const std::optional<std::string> &base_url,
const std::optional<std::string> &path,
const std::optional<std::map<std::string, std::string>> &custom_http_headers) {
App::App(const configuration& config) {
#if QT_CORE_LIB
util::Scheduler::set_default_factory(util::make_qt);
#endif
SyncClientConfig config;
SyncClientConfig client_config;

#if REALM_PLATFORM_APPLE
bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION");
#if REALM_DISABLE_METADATA_ENCRYPTION
config.metadata_mode = SyncManager::MetadataMode::NoEncryption;
#else
config.metadata_mode = SyncManager::MetadataMode::NoEncryption;
bool should_encrypt = config.metadata_encryption_key && !getenv("REALM_DISABLE_METADATA_ENCRYPTION");
#endif
client_config.metadata_mode = should_encrypt ? SyncManager::MetadataMode::Encryption : SyncManager::MetadataMode::NoEncryption;
if (config.metadata_encryption_key) {
auto key = std::vector<char>();
key.resize(64);
key.assign(config.metadata_encryption_key->begin(), config.metadata_encryption_key->end());
client_config.custom_encryption_key = std::move(key);
}

#ifdef QT_CORE_LIB
auto qt_path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();
if (!std::filesystem::exists(qt_path)) {
std::filesystem::create_directory(qt_path);
}
config.base_file_path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();
#else
if (path) {
config.base_file_path = *path;
if (config.path) {
client_config.base_file_path = *config.path;
} else {
config.base_file_path = std::filesystem::current_path().make_preferred().generic_string();
client_config.base_file_path = std::filesystem::current_path().make_preferred().generic_string();
}
#endif
config.user_agent_binding_info = "RealmCpp/0.0.1";
config.user_agent_application_info = app_id;
client_config.user_agent_binding_info = "RealmCpp/0.0.1";
client_config.user_agent_application_info = config.app_id;

auto app_config = app::App::Config();
app_config.app_id = app_id;
app_config.transport = std::make_shared<internal::DefaultTransport>(custom_http_headers);
app_config.base_url = base_url ? base_url : util::Optional<std::string>();
app_config.app_id = config.app_id;
app_config.transport = std::make_shared<internal::DefaultTransport>(config.custom_http_headers);
app_config.base_url = config.base_url;
auto device_info = app::App::Config::DeviceInfo();

device_info.framework_name = "Realm Cpp",
Expand All @@ -385,7 +390,19 @@ namespace realm {
device_info.sdk = "Realm Cpp";
app_config.device_info = std::move(device_info);

m_app = app::App::get_shared_app(std::move(app_config), config);
m_app = app::App::get_shared_app(std::move(app_config), client_config);
}

App::App(const std::string &app_id,
const std::optional<std::string> &base_url,
const std::optional<std::string> &path,
const std::optional<std::map<std::string, std::string>> &custom_http_headers) {
configuration c;
c.app_id = app_id;
c.base_url = base_url;
c.path = path;
c.custom_http_headers = custom_http_headers;
*this = App(std::move(c));
}

std::future<void> App::register_user(const std::string &username, const std::string &password) {
Expand Down
11 changes: 11 additions & 0 deletions src/cpprealm/app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,22 @@ bool operator!=(const user& lhs, const user& rhs);

class App {
public:
struct configuration {
std::string app_id;
std::optional<std::string> base_url;
std::optional<std::string> path;
std::optional<std::map<std::string, std::string>> custom_http_headers;
std::optional<std::array<char, 64>> metadata_encryption_key;
};

[[deprecated("Use App(const configuration&) instead.")]]
explicit App(const std::string& app_id,
const std::optional<std::string>& base_url = {},
const std::optional<std::string>& path = {},
const std::optional<std::map<std::string, std::string>>& custom_http_headers = {});

App(const configuration&);

struct credentials {
using auth_code = util::TaggedString<class auth_code_tag>;
using id_token = util::TaggedString<class id_token_tag>;
Expand Down
7 changes: 7 additions & 0 deletions src/cpprealm/internal/bridge/realm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ namespace realm::internal::bridge {
reinterpret_cast<RealmConfig*>(&m_config)->schema_version = version;
}

void realm::config::set_encryption_key(const std::array<char, 64>& encryption_key) {
auto key = std::vector<char>();
key.resize(64);
key.assign(encryption_key.begin(), encryption_key.end());
reinterpret_cast<RealmConfig*>(&m_config)->encryption_key = std::move(key);
}

realm::sync_config realm::config::sync_config() const {
return reinterpret_cast<const RealmConfig*>(&m_config)->sync_config;
}
Expand Down
1 change: 1 addition & 0 deletions src/cpprealm/internal/bridge/realm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ namespace realm::internal::bridge {
void set_sync_config(const std::optional<struct sync_config>&);
void set_custom_http_headers(const std::map<std::string, std::string>& headers);
void set_schema_version(uint64_t version);
void set_encryption_key(const std::array<char, 64>&);
std::optional<schema> get_schema();
private:
#ifdef __i386__
Expand Down
2 changes: 1 addition & 1 deletion tests/alpha/app_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ static std::string create_jwt(const std::string& appId)
}

TEST_CASE("app", "[app]") {
auto app = realm::App(Admin::shared().cached_app_id(), Admin::shared().base_url());
auto app = realm::App(realm::App::configuration({Admin::shared().cached_app_id(), Admin::shared().base_url()}));

SECTION("auth_providers_promise") {
auto run_login = [&app](realm::App::credentials&& credentials) {
Expand Down
2 changes: 1 addition & 1 deletion tests/alpha/asymmetric_object_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ using namespace realm;
TEST_CASE("asymmetric object", "[sync]") {
SECTION("basic", "[sync]") {
auto asymmetric_app_id = Admin::shared().create_app({}, "test", true);
auto app = realm::App(asymmetric_app_id, Admin::shared().base_url());
auto app = realm::App(realm::App::configuration({asymmetric_app_id, Admin::shared().base_url()}));
auto user = app.login(realm::App::credentials::anonymous()).get();
auto p = realm::async_open<AllTypesAsymmetricObject, EmbeddedFoo>(user.flexible_sync_configuration());
auto tsr = p.get_future().get();
Expand Down
4 changes: 2 additions & 2 deletions tests/alpha/flx_sync_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using namespace realm;

TEST_CASE("flx_sync", "[sync]") {
auto app = realm::App(Admin::shared().cached_app_id(), Admin::shared().base_url());
auto app = realm::App(realm::App::configuration({Admin::shared().cached_app_id(), Admin::shared().base_url()}));
SECTION("all") {
app.get_sync_manager().set_log_level(logger::level::off);
auto user = app.login(realm::App::credentials::anonymous()).get();
Expand Down Expand Up @@ -92,7 +92,7 @@ TEST_CASE("flx_sync", "[sync]") {
}

TEST_CASE("realm_is_populated_on_async_open", "[sync]") {
auto app = realm::App(Admin::shared().cached_app_id(), Admin::shared().base_url());
auto app = realm::App(realm::App::configuration({Admin::shared().cached_app_id(), Admin::shared().base_url()}));
SECTION("all") {
{
auto user = app.login(realm::App::credentials::anonymous()).get();
Expand Down
15 changes: 15 additions & 0 deletions tests/experimental/db/realm_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,19 @@ namespace realm::experimental {
t.join();
p.get_future().get();
}

TEST_CASE("encrypted realm") {
std::array<char, 64> example_key = {0,0,0,0,0,0,0,0, 1,1,0,0,0,0,0,0, 2,2,0,0,0,0,0,0, 3,3,0,0,0,0,0,0, 4,4,0,0,0,0,0,0, 5,5,0,0,0,0,0,0, 6,6,0,0,0,0,0,0, 7,7,0,0,0,0,0,0};
realm_path path;

auto config = realm::db_config();
config.set_encryption_key(example_key);
config.set_path(path);
auto realm = experimental::db(config);

// Missing encryption key
auto config2 = realm::db_config();
config2.set_path(path);
REQUIRE_THROWS(experimental::db(config2));
}
}
4 changes: 2 additions & 2 deletions tests/experimental/sync/app_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using namespace realm;

TEST_CASE("app", "[sync]") {
auto app = realm::App(Admin::shared().cached_app_id(), Admin::shared().base_url());
auto app = realm::App(realm::App::configuration({Admin::shared().cached_app_id(), Admin::shared().base_url()}));

SECTION("get_current_user") {
auto user = app.login(realm::App::credentials::anonymous()).get();
Expand All @@ -31,7 +31,7 @@ TEST_CASE("app", "[sync]") {

SECTION("clear_cached_apps") {
auto temp_app_id = Admin::shared().create_app({"str_col", "_id"});
auto temp_app = realm::App(temp_app_id, Admin::shared().base_url());
auto temp_app = realm::App(realm::App::configuration({temp_app_id, Admin::shared().base_url()}));
auto cached_app = temp_app.get_cached_app(temp_app_id, Admin::shared().base_url());
CHECK(cached_app.has_value());
app.clear_cached_apps();
Expand Down
2 changes: 1 addition & 1 deletion tests/experimental/sync/asymmetric_object_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ using namespace realm;
TEST_CASE("asymmetric object", "[sync_beta]") {
SECTION("basic", "[sync]") {
auto asymmetric_app_id = Admin::shared().create_app({}, "test", true);
auto app = realm::App(asymmetric_app_id, Admin::shared().base_url());
auto app = realm::App(realm::App::configuration({asymmetric_app_id, Admin::shared().base_url()}));
auto user = app.login(realm::App::credentials::anonymous()).get();
auto synced_realm = experimental::open<experimental::AllTypesAsymmetricObject, experimental::EmbeddedFoo>(user.flexible_sync_configuration());

Expand Down
24 changes: 23 additions & 1 deletion tests/experimental/sync/flexible_sync_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using namespace realm;

TEST_CASE("flexible_sync_beta", "[sync]") {
auto app = realm::App(Admin::shared().cached_app_id(), Admin::shared().base_url());
auto app = realm::App(realm::App::configuration({Admin::shared().cached_app_id(), Admin::shared().base_url()}));
SECTION("all") {
app.get_sync_manager().set_log_level(logger::level::all);
auto user = app.login(realm::App::credentials::anonymous()).get();
Expand Down Expand Up @@ -75,4 +75,26 @@ TEST_CASE("flexible_sync_beta", "[sync]") {
objs = synced_realm.objects<experimental::AllTypesObject>();
CHECK(objs.size() == 1);
}

SECTION("encrypted sync realm") {
std::array<char, 64> example_key = {0,0,0,0,0,0,0,0, 1,1,0,0,0,0,0,0, 2,2,0,0,0,0,0,0, 3,3,0,0,0,0,0,0, 4,4,0,0,0,0,0,0, 5,5,0,0,0,0,0,0, 6,6,0,0,0,0,0,0, 7,7,0,0,0,0,0,0};
realm::App::configuration app_config;
app_config.app_id = Admin::shared().create_app({"str_col", "_id"});
app_config.base_url = Admin::shared().base_url();
app_config.metadata_encryption_key = example_key;
auto encrypted_app = realm::App(app_config);
auto user = encrypted_app.login(realm::App::credentials::anonymous()).get();
auto flx_sync_config = user.flexible_sync_configuration();
flx_sync_config.set_encryption_key(example_key);
auto synced_realm = experimental::db(flx_sync_config);

auto update_success = synced_realm.subscriptions().update([](realm::mutable_sync_subscription_set &subs) {
subs.clear();
}).get();
CHECK(update_success == true);
CHECK(synced_realm.subscriptions().size() == 0);
// Missing encryption key
auto flx_sync_config2 = user.flexible_sync_configuration();
REQUIRE_THROWS(experimental::db(flx_sync_config2));
}
}
Loading