diff --git a/examples/cpp/beta/local/CMakeLists.txt b/examples/cpp/beta/local/CMakeLists.txt index 545c83b50e..d4d309290f 100644 --- a/examples/cpp/beta/local/CMakeLists.txt +++ b/examples/cpp/beta/local/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(examples-beta-local crud.cpp filter-data.cpp notifications.cpp + realm-files.cpp relationships.cpp supported-types.cpp threading.cpp diff --git a/examples/cpp/beta/local/realm-files.cpp b/examples/cpp/beta/local/realm-files.cpp new file mode 100644 index 0000000000..3ae86c2cc0 --- /dev/null +++ b/examples/cpp/beta/local/realm-files.cpp @@ -0,0 +1,70 @@ +#include +#include +#include + +using namespace realm::experimental; + +struct Dog { + std::string name; + int64_t age; +}; +REALM_SCHEMA(Dog, name, age) + +TEST_CASE("Encrypt a realm example", "[write]") { + auto relative_realm_path_directory = "beta_encrypt-realm/"; + std::filesystem::create_directories(relative_realm_path_directory); + std::filesystem::path path = std::filesystem::current_path().append(relative_realm_path_directory); + path = path.append("encrypted"); + path = path.replace_extension("realm"); + // :snippet-start: beta-open-encrypted-realm + // Check if we already have a key stored in the platform's secure storage. + // If we don't, generate a new one. + // Use your preferred method to generate a key. This example key is + // NOT representative of a secure encryption key. It only exists to + // illustrate the form your key might take. + std::array exampleKey = { + 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 + }; + + // Store the key securely to be used next time we want to open the database. + // We don't illustrate this here because it varies depending on the platform. + + // Create a database configuration. + auto config = realm::db_config(); + config.set_path(path); // :remove: + // Set the encryption key in your config. + config.set_encryption_key(exampleKey); + + // Open or create a realm with the config containing the encryption key. + auto realm = db(config); + // :snippet-end: + + auto dog = Dog { + .name = "Maui", + .age = 3 + }; + + realm.write([&] { + realm.add(std::move(dog)); + }); + + auto managedDogs = realm.objects(); + auto specificDog = managedDogs[0]; + REQUIRE(specificDog.name == "Maui"); + REQUIRE(specificDog.age == static_cast(3)); + REQUIRE(managedDogs.size() == 1); + + realm.write([&] { + realm.remove(specificDog); + }); + + auto managedDogsAfterDelete = realm.objects(); + REQUIRE(managedDogsAfterDelete.size() == 0); +} diff --git a/examples/cpp/beta/sync/CMakeLists.txt b/examples/cpp/beta/sync/CMakeLists.txt index eb209e83be..eed1aaddc6 100644 --- a/examples/cpp/beta/sync/CMakeLists.txt +++ b/examples/cpp/beta/sync/CMakeLists.txt @@ -14,7 +14,7 @@ FetchContent_Declare( FetchContent_Declare( cpprealm GIT_REPOSITORY https://github.com/realm/realm-cpp.git - GIT_TAG v0.3.0-preview + GIT_TAG v0.4.0-preview ) FetchContent_MakeAvailable(Catch2 cpprealm) diff --git a/examples/cpp/beta/sync/app.cpp b/examples/cpp/beta/sync/app.cpp index 722a26a312..92e0676505 100644 --- a/examples/cpp/beta/sync/app.cpp +++ b/examples/cpp/beta/sync/app.cpp @@ -1,5 +1,8 @@ #include #include +#include + +using namespace realm::experimental; static const std::string APP_ID = "cpp-tester-uliix"; @@ -8,7 +11,7 @@ TEST_CASE("test custom headers compile", "[realm][sync]") { std::map customHttpHeaders; customHttpHeaders.emplace("CUSTOM_HEADER_NAME", "CUSTOM_HEADER_VALUE"); - auto app = realm::App(APP_ID, std::nullopt, std::nullopt, customHttpHeaders); + auto app = realm::App(realm::App::configuration({APP_ID, std::nullopt, std::nullopt, customHttpHeaders})); // :snippet-end: app.get_sync_manager().set_log_level(realm::logger::level::warn); @@ -25,3 +28,88 @@ TEST_CASE("test custom headers compile", "[realm][sync]") { user.log_out().get(); REQUIRE(user.access_token().empty()); } + +struct Beta_FlexibleSync_Dog { + realm::experimental::primary_key _id{realm::object_id::generate()}; + std::string name; + int64_t age; +}; +REALM_SCHEMA(Beta_FlexibleSync_Dog, _id, name, age) + +TEST_CASE("test metadata encryption", "[realm][sync]") { + // :snippet-start: encrypt-metadata + // Check if we already have a key stored in the platform's secure storage. + // If we don't, generate a new one. + // Use your preferred method to generate a key. This example key is + // NOT representative of a secure encryption key. It only exists to + // illustrate the form your key might take. + std::array exampleKey = { + 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 + }; + + // Create and populate an App configuration. + auto appConfig = realm::App::configuration(); + appConfig.app_id = APP_ID; + // Specify the metadata key. + appConfig.metadata_encryption_key = exampleKey; + + // Use the configuration when you open the app. + auto app = realm::App(appConfig); + // :snippet-end: + + // The code from here down isn't relevant to the example - just confirming + // we can open and write to a realm whose metadata is encrypted. + app.get_sync_manager().set_log_level(realm::logger::level::warn); + + auto user = app.login(realm::App::credentials::anonymous()).get(); + REQUIRE(user.is_logged_in()); + + auto syncConfig = user.flexible_sync_configuration(); + + auto syncedRealm = realm::experimental::db(syncConfig); + + syncedRealm.subscriptions().update([](realm::mutable_sync_subscription_set &subs) { + subs.add("puppies", [](auto &obj) { + return obj.age < 3; + }); + }).get(); + + auto objectId = realm::object_id::generate(); + auto maui = Beta_FlexibleSync_Dog(); + maui._id = objectId; + maui.name = "Maui"; + maui.age = 2; + + syncedRealm.write([&] { + syncedRealm.add(std::move(maui)); + }); + + auto syncSession = syncedRealm.get_sync_session(); + syncSession->wait_for_upload_completion().get(); + + auto dogs = syncedRealm.objects(); + REQUIRE(dogs.size() == 1); + auto dogsNamedMaui = dogs.where([](auto &dog) { + return dog.name == "Maui"; + }); + auto persistedMaui = dogsNamedMaui[0]; + + syncedRealm.write([&] { + syncedRealm.remove(persistedMaui); + }); + + auto managedDogsAfterDelete = syncedRealm.objects(); + REQUIRE(managedDogsAfterDelete.size() == 0); + + syncSession->wait_for_upload_completion().get(); + + user.log_out().get(); + REQUIRE(user.access_token().empty()); +} diff --git a/source/examples/generated/cpp/app.snippet.encrypt-metadata.cpp b/source/examples/generated/cpp/app.snippet.encrypt-metadata.cpp new file mode 100644 index 0000000000..4e66622842 --- /dev/null +++ b/source/examples/generated/cpp/app.snippet.encrypt-metadata.cpp @@ -0,0 +1,24 @@ +// Check if we already have a key stored in the platform's secure storage. +// If we don't, generate a new one. +// Use your preferred method to generate a key. This example key is +// NOT representative of a secure encryption key. It only exists to +// illustrate the form your key might take. +std::array exampleKey = { + 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 +}; + +// Create and populate an App configuration. +auto appConfig = realm::App::configuration(); +appConfig.app_id = APP_ID; +// Specify the metadata key. +appConfig.metadata_encryption_key = exampleKey; + +// Use the configuration when you open the app. +auto app = realm::App(appConfig); diff --git a/source/examples/generated/cpp/app.snippet.set-custom-headers-for-app.cpp b/source/examples/generated/cpp/app.snippet.set-custom-headers-for-app.cpp index ef89d8a273..b210965d63 100644 --- a/source/examples/generated/cpp/app.snippet.set-custom-headers-for-app.cpp +++ b/source/examples/generated/cpp/app.snippet.set-custom-headers-for-app.cpp @@ -1,4 +1,4 @@ std::map customHttpHeaders; customHttpHeaders.emplace("CUSTOM_HEADER_NAME", "CUSTOM_HEADER_VALUE"); -auto app = realm::App(APP_ID, std::nullopt, std::nullopt, customHttpHeaders); +auto app = realm::App(realm::App::configuration({APP_ID, std::nullopt, std::nullopt, customHttpHeaders})); diff --git a/source/examples/generated/cpp/realm-files.snippet.beta-open-encrypted-realm.cpp b/source/examples/generated/cpp/realm-files.snippet.beta-open-encrypted-realm.cpp new file mode 100644 index 0000000000..e1f7778ce6 --- /dev/null +++ b/source/examples/generated/cpp/realm-files.snippet.beta-open-encrypted-realm.cpp @@ -0,0 +1,26 @@ +// Check if we already have a key stored in the platform's secure storage. +// If we don't, generate a new one. +// Use your preferred method to generate a key. This example key is +// NOT representative of a secure encryption key. It only exists to +// illustrate the form your key might take. +std::array exampleKey = { + 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 +}; + +// Store the key securely to be used next time we want to open the database. +// We don't illustrate this here because it varies depending on the platform. + +// Create a database configuration. +auto config = realm::db_config(); +// Set the encryption key in your config. +config.set_encryption_key(exampleKey); + +// Open or create a realm with the config containing the encryption key. +auto realm = db(config); diff --git a/source/sdk/cpp.txt b/source/sdk/cpp.txt index 25d221fc80..806eb6d793 100644 --- a/source/sdk/cpp.txt +++ b/source/sdk/cpp.txt @@ -15,7 +15,7 @@ C++ SDK Preview Install Realm Quick Start Model Data - Configure & Open a Realm + Realm Files CRUD React to Changes Application Services diff --git a/source/sdk/cpp/app-services/connect-to-app.txt b/source/sdk/cpp/app-services/connect-to-app.txt index 866247758e..fd362d95b1 100644 --- a/source/sdk/cpp/app-services/connect-to-app.txt +++ b/source/sdk/cpp/app-services/connect-to-app.txt @@ -47,13 +47,14 @@ Access the App Client Set Custom HTTP Headers ----------------------- -.. versionadded:: v0.3.0-preview +.. versionchanged:: v0.4.0-preview replaces deprecated realm::App(...) with realm::App(const realm::App::configuration&) If you use App Services or Device Sync with a proxy setup, you may need to set custom HTTP headers. The Realm C++ SDK supports setting custom -HTTP headers on the ``App``, and on the ``flexible_sync_configuration()``. +HTTP headers on the :cpp-sdk:`App ` +and on the ``flexible_sync_configuration()``. -When you initialize the ``App``, pass in a map of string header keys +When you initialize the App configuration, pass in a map of string header keys and values. .. literalinclude:: /examples/generated/cpp/app.snippet.set-custom-headers-for-app.cpp @@ -65,3 +66,28 @@ these custom headers. If you want to use custom headers with Device Sync, you must additionally :ref:`set the headers on the Flexible Sync configuration `. + +.. _cpp-encrypt-app-metadata: + +Encrypt App Metadata +-------------------- + +.. versionadded:: v0.4.0-preview + +When you connect to App Services, Realm creates additional metadata files +on a device. For more information about these metadata files, refer to +:ref:`cpp-realm-database`. + +You can encrypt the metadata that App Services stores on client devices, +similar to :ref:`encrypting the realm `. + +On Apple devices, the metadata is encrypted by default. To disable this, +add ``REALM_DISABLE_METADATA_ENCRYPTION`` to your environment variables. + +To enable metadata encryption on other platforms, you must set a +``metadata_encryption_key`` on your +:cpp-sdk:`realm::App::configuration `. + +.. literalinclude:: /examples/generated/cpp/app.snippet.encrypt-metadata.cpp + :language: cpp + :emphasize-lines: 20-21 diff --git a/source/sdk/cpp/realm-database.txt b/source/sdk/cpp/realm-database.txt index 4b812cd466..b387dcaf36 100644 --- a/source/sdk/cpp/realm-database.txt +++ b/source/sdk/cpp/realm-database.txt @@ -1,11 +1,9 @@ .. _cpp-realm-database: - ======================= Realm - C++ SDK Preview ======================= - .. contents:: On this page :local: :backlinks: none diff --git a/source/sdk/cpp/realm-files.txt b/source/sdk/cpp/realm-files.txt new file mode 100644 index 0000000000..4ec865cb8a --- /dev/null +++ b/source/sdk/cpp/realm-files.txt @@ -0,0 +1,11 @@ +.. _cpp-realms: + +======================================= +Work with Realm Files - C++ SDK Preview +======================================= + +.. toctree:: + :titlesonly: + + Configure & Open a Realm + Encrypt a Realm diff --git a/source/sdk/cpp/realm-files/encrypt-a-realm.txt b/source/sdk/cpp/realm-files/encrypt-a-realm.txt new file mode 100644 index 0000000000..02d4f2d65f --- /dev/null +++ b/source/sdk/cpp/realm-files/encrypt-a-realm.txt @@ -0,0 +1,74 @@ +.. _cpp-encrypt-a-realm: + +================================= +Encrypt a Realm - C++ SDK Preview +================================= + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +You can encrypt the realm file on disk with AES-256 + +SHA-2 by supplying a 64-byte encryption key when :ref:`opening a +realm `. + +Realm transparently encrypts and decrypts data with standard +:wikipedia:`AES-256 encryption ` using the +first 256 bits of the given 512-bit encryption key. Realm +uses the other 256 bits of the 512-bit encryption key to validate +integrity using a :wikipedia:`hash-based message authentication code +(HMAC) `. + +.. include:: /includes/encrypt-use-strong-cryptographic-hash.rst + +.. versionadded:: v0.4.0-preview + +Encrypt a realm by calling the ``set_encryption_key()`` function on +your :cpp-sdk:`db_config `: + +.. literalinclude:: /examples/generated/cpp/realm-files.snippet.beta-open-encrypted-realm.cpp + :language: cpp + :emphasize-lines: 22-23 + +.. tip:: You cannot encrypt a realm that already exists on device + + The C++ SDK Preview does not yet support encrypting a realm that already + exists on device. You must encrypt the realm the first time you open it. + +Store & Reuse Keys +------------------ + +You **must** pass the same encryption key every time you open the encrypted realm. +If you don't provide a key or specify the wrong key for an encrypted +realm, the Realm SDK throws an error. + +Apps should store the encryption key securely on the device so that other +apps cannot read the key. + +Performance Impact +------------------ + +Reads and writes on encrypted realms can be up to 10% slower than unencrypted realms. + +Encryption and Atlas Device Sync +-------------------------------- + +Encrypt a Synced Realm +~~~~~~~~~~~~~~~~~~~~~~ + +You can encrypt a :ref:`synced realm `. + +.. include:: /includes/encrypt-atlas-device-sync.rst + +If you need unique keys for each user of your application, you can use an OAuth provider or +use one of the :ref:`Realm authentication providers ` +and an :ref:`authentication trigger` +to create a 64-bit key and store that key in a :ref:`user object `. + +Encrypt Metadata +~~~~~~~~~~~~~~~~ + +You can encrypt the metadata that Realm stores on the device. For more +information, refer to :ref:`cpp-encrypt-app-metadata`.