Skip to content

Commit

Permalink
Update encrypt documentation with App metadata details
Browse files Browse the repository at this point in the history
  • Loading branch information
cbullinger committed Nov 9, 2023
1 parent 30e51bd commit 7fea56d
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,47 @@ class AppClientTest: RealmTest() {
}

@Test
fun setCustomHttpHeadersTest() {
val myEncryptionKey = getEncryptionKey()
val config1 =
// :snippet-start: configure-app-client
fun configureAppClient() {
val config =
// :snippet-start: configure-app-client
// Creates an App with custom configuration values
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
// Specify your custom configuration values
.appName("my-app-name")
.appVersion("1.0.0")
.baseUrl("http://localhost:9090")
.build()
// :snippet-end:
assertEquals(config.appName, "my-app-name")
assertEquals(config.baseUrl, "http://localhost:9090")
assertEquals(config.appVersion, "1.0.0")
}

@Test
fun encryptAppMetadata() {
val myEncryptionKey = getEncryptionKey()
val config =
// :snippet-start: encrypted-app-client
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
// Specify the encryption key
.encryptionKey(myEncryptionKey)
.build()
// :snippet-end:
assertEquals(config.encryptionKey, myEncryptionKey)
}

@Test
fun setCustomHttpHeadersTest() {
val myEncryptionKey = getEncryptionKey()
val config1 = AppConfiguration.Builder(YOUR_APP_ID)
.appName("my-app-name")
.encryptionKey(myEncryptionKey)
.build()
val config2 =
// :snippet-start: set-custom-http-headers
AppConfiguration.Builder(YOUR_APP_ID)
.authorizationHeaderName("MyApp-Authorization")
.customRequestHeaders {
put("X-MyApp-Version", "1.0.0")
}
.customRequestHeaders { put("X-MyApp-Version", "1.0.0") }
.build()
// :snippet-end:
assertEquals(config1.authorizationHeaderName, "Authorization")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package com.mongodb.realm.realmkmmapp

import io.realm.kotlin.Realm
import io.realm.kotlin.RealmConfiguration
import io.realm.kotlin.ext.query
import io.realm.kotlin.internal.platform.runBlocking
import io.realm.kotlin.mongodb.App
import io.realm.kotlin.mongodb.Credentials
import io.realm.kotlin.mongodb.sync.SyncConfiguration
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class EncryptARealmTest : RealmTest() {
@Test
Expand All @@ -15,39 +21,62 @@ class EncryptARealmTest : RealmTest() {
// generate a new 64-byte encryption key
val key = ByteArray(64)
if (seed != null) {
// If there is a seed provided, create a random number with that seed and fill the byte array with random bytes
// If there is a seed provided, create a random number with that seed
// and fill the byte array with random bytes
Random(seed).nextBytes(key)
} else {
// fill the byte array with random bytes
Random.nextBytes(key)
}
return key
}
// :remove-start:
assertTrue(getRandomKey().size == 64)
val generatedKey = getRandomKey()
// :remove-end:

runBlocking {
// Create the configuration
val config = RealmConfiguration.Builder(
setOf(Frog::class))
val config = RealmConfiguration.Builder(setOf(Frog::class))
// :remove-start:
.deleteRealmIfMigrationNeeded()
.directory("tmp/encrypted")
// :remove-end:
// specify the encryptionKey
.encryptionKey(getRandomKey())
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
// :remove-start:
Realm.deleteRealm(config)
// :remove-end:
// Open a realm with the encryption key.
// Open the realm with the configuration
val realm = Realm.open(config)
Log.v("Successfully opened realm:" +
realm.configuration.name
)

Log.v("Successfully opened encrypted realm:" + realm.configuration.name)
// :remove-start:
assertEquals(generatedKey, realm.configuration.encryptionKey)
realm.close()
Realm.deleteRealm(config)
// :remove-end:
}
// :snippet-end:
}

@Test
fun encryptSyncedRealm() {
val generatedKey = getEncryptionKey()
val app = App.create(yourFlexAppId)
runBlocking {
val user = app.login(Credentials.anonymous())
// :snippet-start: encrypt-synced-realm
val config = SyncConfiguration.Builder(user, setOf(Frog::class))
.initialSubscriptions { realm ->
add(realm.query<Frog>())
}
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
val realm = Realm.open(config)
Log.v("Successfully opened encrypted realm: ${realm.configuration}")
// :snippet-end:
assertEquals(generatedKey, realm.configuration.encryptionKey)
realm.close()
user.delete()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Creates an App with custom configuration values
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
// Specify your custom configuration values
.appName("my-app-name")
.encryptionKey(myEncryptionKey)
.appVersion("1.0.0")
.baseUrl("http://localhost:9090")
.build()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
// Specify the encryption key
.encryptionKey(myEncryptionKey)
.build()
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
AppConfiguration.Builder(YOUR_APP_ID)
.authorizationHeaderName("MyApp-Authorization")
.customRequestHeaders {
put("X-MyApp-Version", "1.0.0")
}
.customRequestHeaders { put("X-MyApp-Version", "1.0.0") }
.build()
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ fun getRandomKey(seed: Long? = null): ByteArray {
// generate a new 64-byte encryption key
val key = ByteArray(64)
if (seed != null) {
// If there is a seed provided, create a random number with that seed and fill the byte array with random bytes
// If there is a seed provided, create a random number with that seed
// and fill the byte array with random bytes
Random(seed).nextBytes(key)
} else {
// fill the byte array with random bytes
Expand All @@ -14,15 +15,11 @@ fun getRandomKey(seed: Long? = null): ByteArray {

runBlocking {
// Create the configuration
val config = RealmConfiguration.Builder(
setOf(Frog::class))
// specify the encryptionKey
.encryptionKey(getRandomKey())
val config = RealmConfiguration.Builder(setOf(Frog::class))
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
// Open a realm with the encryption key.
// Open the realm with the configuration
val realm = Realm.open(config)
Log.v("Successfully opened realm:" +
realm.configuration.name
)

Log.v("Successfully opened encrypted realm:" + realm.configuration.name)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
val config = SyncConfiguration.Builder(user, setOf(Frog::class))
.initialSubscriptions { realm ->
add(realm.query<Frog>())
}
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
val realm = Realm.open(config)
Log.v("Successfully opened encrypted realm: ${realm.configuration}")
32 changes: 28 additions & 4 deletions source/sdk/kotlin/app-services/connect-to-app-services-backend.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,44 @@ Configure the App Client

You can add optional arguments to the ``AppConfiguration`` for more
granular control of your app connection details, such as custom
timeouts for connections and keys for local encryption.
request headers and keys for local encryption.

To control the additional configuration options, use the
`AppConfiguration.Builder
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__ and call the ``.build()`` method to pass a configuration
object:
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__
and call the ``.build()`` method to pass a configuration object:

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt
:language: kotlin

.. include:: /includes/multiple-app-client-details-and-app-config-cache.rst

.. _kotlin-encrypt-app-metadata:

Encrypt App Metadata
~~~~~~~~~~~~~~~~~~~~

When you connect to App Services, Realm creates additional metadata files on
a device. For more information about these metadata files, refer to
:ref:`<kotlin-realm-database-internals>`.

You can encrypt the metadata that App Services stores on client devices,
similar to how you :ref:`<kotlin-encrypt-a-synced-realm>`.

To encrypt App metadata, pass your encryption key to the
`encryptionKey <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/encryption-key.html>`__
property when you initialize the App:

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.encrypted-app-client.kt
:language: kotlin

.. note:: Metadata Encrypted By Default on Apple Devices

On Apple devices, metadata is encrypted by default. To disable this,
add ``REALM_DISABLE_METADATA_ENCRYPTION`` to your environment variables.

Set Custom HTTP Headers
-----------------------
~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 1.11.0

Expand Down
81 changes: 55 additions & 26 deletions source/sdk/kotlin/realm-database/realm-files/encrypt-a-realm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ Encrypt a Realm - Kotlin SDK
:depth: 2
:class: singlecol

Overview
--------

You can encrypt your realms to ensure that the data stored to disk can't be
read outside of your application. You encrypt the realm file on
disk with AES-256 + SHA-2 by supplying a 64-byte encryption key when
Expand All @@ -27,13 +24,49 @@ integrity using a :wikipedia:`hash-based message authentication code

.. include:: /includes/encrypt-use-strong-cryptographic-hash.rst

Encrypt a Local Realm
---------------------

To encrypt a local realm, pass your encryption key to the
`encryptionKey <{+kotlin-local-prefix+}io.realm.kotlin/-configuration/encryption-key.html>`__
property in your
`RealmConfiguration.Builder() <{+kotlin-local-prefix+}io.realm.kotlin/-realm-configuration/index.html>`__.

The following code demonstrates how to generate an encryption key and
open an encrypted local realm:

.. literalinclude:: /examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt
:language: kotlin
:emphasize-lines: 19-20

.. _kotlin-encrypt-a-synced-realm:

Encrypt a Synced Realm
----------------------

You can also encrypt a :ref:`synced realm <kotlin-open-a-synced-realm>`, similar
to encrypting a local realm. Refer to the :ref:`<kotlin-encryption-and-device-sync>`
section on this page for more information.

To encrypt a synced realm, pass your encryption key to the
`encryptionKey <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/encryption-key.html>`__
property in your
`SyncConfiguration.Builder()
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb.sync/-sync-configuration/-builder/-builder.html>`__.

The following code demonstrates how to open an encrypted synced realm:

.. literalinclude:: /examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-synced-realm.kt
:language: kotlin
:emphasize-lines: 5-6

Considerations
--------------

The following are key impacts to consider when encrypting a realm.

Storing & Reusing Keys
~~~~~~~~~~~~~~~~~~~~~~
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
Expand All @@ -53,20 +86,8 @@ Performance Impact

Reads and writes on encrypted realms can be up to 10% slower than unencrypted realms.

Encryption and Atlas Device Sync
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can encrypt a :ref:`synced realm <kotlin-open-a-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 <users-and-authentication>`
and an :ref:`authentication trigger<authentication-triggers>`
to create a 64-bit key and store that key in a :ref:`user object <user-objects>`.

Multiple Processes
~~~~~~~~~~~~~~~~~~
Access an Encrypted Realm from Multiple Processes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionchanged:: 10.8.0

Expand All @@ -77,13 +98,21 @@ If your app uses Realm Kotlin SDK version 10.7.1 or earlier, attempting to
open an encrypted realm from multiple processes throws this error:
``Encrypted interprocess sharing is currently unsupported.``

Example
-------
.. _kotlin-encryption-and-device-sync:

.. include:: /includes/encrypt-use-strong-cryptographic-hash.rst
Encryption and Atlas Device Sync
--------------------------------

The following code demonstrates how to generate an encryption key and
open an encrypted realm:
.. include:: /includes/encrypt-atlas-device-sync.rst

.. literalinclude:: /examples/generated/kotlin/EncryptARealmTest.snippet.encrypt-a-realm.kt
:language: kotlin
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 <users-and-authentication>`
and an :ref:`authentication trigger<authentication-triggers>`
to create a 64-bit key and store that key in a :ref:`user object <user-objects>`.

Encrypt App Metadata
~~~~~~~~~~~~~~~~~~~~

You can encrypt the App Services App metadata that Realm stores on the device.

To learn more, refer to :ref:`<kotlin-encrypt-app-metadata>`.

0 comments on commit 7fea56d

Please sign in to comment.