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

DRIVERS-2789 Fix Math Rendering in Client Side Encryption Spec #1557

Merged
merged 5 commits into from
Mar 25, 2024
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
154 changes: 77 additions & 77 deletions source/client-side-encryption/client-side-encryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,35 +540,35 @@ automatic credentials results in a runtime error from [libmongocrypt](#libmongoc
> [!NOTE]
> Drivers MUST NOT eagerly fill an empty KMS options property.

Once requested, drivers MUST create a new [KMSProviders](#kmsproviders) `$P$` according to the following process:
Once requested, drivers MUST create a new [KMSProviders](#kmsproviders) $P$ according to the following process:

1. Let `$K$` be the [kmsProviders](#kmsproviders) value provided by the user as part of the original
1. Let $K$ be the [kmsProviders](#kmsproviders) value provided by the user as part of the original
[ClientEncryptionOpts](#ClientEncryptionOpts) or [AutoEncryptionOpts](#AutoEncryptionOpts).
2. Initialize `$P$` to an empty [KMSProviders](#kmsproviders) object.
3. If `$K$` contains an `aws` property, and that property is an empty map:
1. Attempt to obtain credentials `$C$` from the environment using similar logic as is detailed in
2. Initialize $P$ to an empty [KMSProviders](#kmsproviders) object.
3. If $K$ contains an `aws` property, and that property is an empty map:
1. Attempt to obtain credentials $C$ from the environment using similar logic as is detailed in
[the obtaining-AWS-credentials section from the Driver Authentication specification](../auth/auth.md#obtaining-credentials),
but ignoring the case of loading the credentials from a URI
2. If credentials `$C$` were successfully loaded, create a new [AWSKMSOptions](#AWSKMSOptions) map from `$C$` and
insert that map onto `$P$` as the `aws` property.
4. If `$K$` contains an `gcp` property, and that property is an empty map:
1. Attempt to obtain credentials `$C$` from the environment logic as is detailed in
2. If credentials $C$ were successfully loaded, create a new [AWSKMSOptions](#AWSKMSOptions) map from $C$ and insert
that map onto $P$ as the `aws` property.
4. If $K$ contains an `gcp` property, and that property is an empty map:
1. Attempt to obtain credentials $C$ from the environment logic as is detailed in
[Obtaining GCP Credentials](#obtaining-gcp-credentials).
2. If credentials `$C$` were successfully loaded, create a new [GCPKMSOptions](#GCPKMSOptions) map from `$C$` and
insert that map onto `$P$` as the `gcp` property.
5. If `$K$` contains an `azure` property, and that property is an empty map:
2. If credentials $C$ were successfully loaded, create a new [GCPKMSOptions](#GCPKMSOptions) map from $C$ and insert
that map onto $P$ as the `gcp` property.
5. If $K$ contains an `azure` property, and that property is an empty map:
1. If there is a `cachedAzureAccessToken` AND the duration until `azureAccessTokenExpireTime` is greater than one
minute, insert `cachedAzureAccessToken` as the `azure` property on `$P$`.
minute, insert `cachedAzureAccessToken` as the `azure` property on $P$.
2. Otherwise:
1. Let `$t_0$` be the current time.
2. Attempt to obtain an Azure VM Managed Identity Access Token `$T$` as detailed in
1. Let $t_0$ be the current time.
2. Attempt to obtain an Azure VM Managed Identity Access Token $T$ as detailed in
[Obtaining an Access Token for Azure Key Vault](#obtaining-an-access-token-for-azure-key-vault).
3. If a token `$T$` with expire duration `$d\_{exp}$` were obtained successfully, create a new
[AzureAccessToken](#AzureAccessToken) object with `$T$` as the `accessToken` property. Insert that
[AzureAccessToken](#AzureAccessToken) object into `$P$` as the `azure` property. Record the generated
3. If a token $T$ with expire duration $d\_{exp}$ were obtained successfully, create a new
[AzureAccessToken](#AzureAccessToken) object with $T$ as the `accessToken` property. Insert that
[AzureAccessToken](#AzureAccessToken) object into $P$ as the `azure` property. Record the generated
[AzureAccessToken](#AzureAccessToken) in `cachedAzureAccessToken`. Record the `azureAccessTokenExpireTime` as
`$t_0 + d\_{exp}$`.
6. Return `$P$` as the additional KMS providers to [libmongocrypt](#libmongocrypt).
$t_0 + d\_{exp}$.
6. Return $P$ as the additional KMS providers to [libmongocrypt](#libmongocrypt).

<span id="obtaining-gcp-credentials" />

Expand Down Expand Up @@ -601,37 +601,37 @@ the VM, an identity can be used by obtaining an access token via HTTP from the *

The below steps should be taken:

01. Let `$U$` be a new URL, initialized from the URL string `"http://169.254.169.254/metadata/identity/oauth2/token"`
01. Let $U$ be a new URL, initialized from the URL string `"http://169.254.169.254/metadata/identity/oauth2/token"`

02. Add a query parameter `api-version=2018-02-01` to `$U$`.
02. Add a query parameter `api-version=2018-02-01` to $U$.

03. Add a query parameter `resource=https://vault.azure.net/` to `$U$`.
03. Add a query parameter `resource=https://vault.azure.net/` to $U$.

04. Prepare an HTTP GET request `$Req$` based on `$U$`.
04. Prepare an HTTP GET request $Req$ based on $U$.

> [!NOTE]
> All query parameters on `$U$` should be appropriately percent-encoded
> All query parameters on $U$ should be appropriately percent-encoded

05. Add HTTP headers `Metadata: true` and `Accept: application/json` to `$Req$`.
05. Add HTTP headers `Metadata: true` and `Accept: application/json` to $Req$.

06. Issue `$Req$` to the Azure IMDS server `169.254.169.254:80`. Let `$Resp$` be the response from the server. If the
HTTP response is not completely received within ten seconds, consider the request to have timed out, and return an
error instead of an access token.
06. Issue $Req$ to the Azure IMDS server `169.254.169.254:80`. Let $Resp$ be the response from the server. If the HTTP
response is not completely received within ten seconds, consider the request to have timed out, and return an error
instead of an access token.

07. If `$Resp_{status} ≠ 200$`, obtaining the access token has failed, and the HTTP response body of `$Resp$` encodes
07. If $Resp\_{status} ≠ 200$, obtaining the access token has failed, and the HTTP response body of $Resp$ encodes
information about the error that occurred. Return an error including the HTTP response body instead of an access
token.

08. Otherwise, let `$J$` be the JSON document encoded in the HTTP response body of `$Resp$`.
08. Otherwise, let $J$ be the JSON document encoded in the HTTP response body of $Resp$.

09. The result access token `$T$` is given as the `access_token` string property of `$J$`. Return `$T$` as the resulting
09. The result access token $T$ is given as the `access_token` string property of $J$. Return $T$ as the resulting
access token.

10. The resulting "expires in" duration `$d_{exp}$` is a count of seconds given as an ASCII-encoded integer string
`expires_in` property of `$J$`.
10. The resulting "expires in" duration $d\_{exp}$ is a count of seconds given as an ASCII-encoded integer string
`expires_in` property of $J$.

> [!NOTE]
> If JSON decoding of `$Resp$` fails, or the `access_token` property is absent from `$J$`, this is a protocol error from
> If JSON decoding of $Resp$ fails, or the `access_token` property is absent from $J$, this is a protocol error from
> IMDS. Indicate this error to the requester of the access token.

> [!NOTE]
Expand Down Expand Up @@ -809,19 +809,19 @@ A collection supporting Queryable Encryption requires an index and three additio
The convenience methods support the following lookup process for finding the `encryptedFields` associated with a
collection.

Assume an exposition-only function `$GetEncryptedFields(opts, collName, dbName, askDb)$`, where `$opts$` is a set of
options, `$collName$` is the name of the collection, `$dbName$` is the name of the database associated with that
collection, and `$askDb$` is a boolean value. The resulting `encryptedFields` `$EF$` is found by:
Assume an exposition-only function $GetEncryptedFields(opts, collName, dbName, askDb)$, where $opts$ is a set of
options, $collName$ is the name of the collection, $dbName$ is the name of the database associated with that collection,
and $askDb$ is a boolean value. The resulting `encryptedFields` $EF$ is found by:

1. Let `$QualName$` be the string formed by joining`$dbName$` and `$collName$` with an ASCII dot `"."`.
2. If `$opts$` contains an `"encryptedFields"` property, then `$EF$` is the value of that property.
3. Otherwise, if `AutoEncryptionOptions.encryptedFieldsMap` contains an element named by `$QualName$`, then `$EF$` is
the value of that element.
4. Otherwise, if `$askDb$` is `true`:
1. Let $QualName$ be the string formed by joining$dbName$ and $collName$ with an ASCII dot `"."`.
2. If $opts$ contains an `"encryptedFields"` property, then $EF$ is the value of that property.
3. Otherwise, if `AutoEncryptionOptions.encryptedFieldsMap` contains an element named by $QualName$, then $EF$ is the
value of that element.
4. Otherwise, if $askDb$ is `true`:
1. Issue a `listCollections` command against the database named by $dbName$, filtered by `{name: <collName>}`. Let
the result be the document `$L$`.
2. If `$L$` contains an `options` document element, and that element contains an `encryptedFields` document element,
`$EF$` is `$L$` `["options"]["encryptedFields"]`.
the result be the document $L$.
2. If $L$ contains an `options` document element, and that element contains an `encryptedFields` document element,
$EF$ is $L$ `["options"]["encryptedFields"]`.
3. Otherwise, $EF$ is *not-found*
5. Otherwise, $EF$ is considered *not-found*.

Expand All @@ -843,8 +843,8 @@ Drivers MUST support a BSON document option named `encryptedFields` for any
> Drivers MUST NOT document the `escCollection` and `ecocCollection` options.

For a helper function, `CreateCollection(collectionName, collectionOptions)` with the name of the database associated as
`$dbName$`, look up the encrypted fields `encryptedFields` for the collection as
`$GetEncryptedFields(collectionOptions, collectionName, dbName, false)$` ([See here](#GetEncryptedFields)).
$dbName$, look up the encrypted fields `encryptedFields` for the collection as $GetEncryptedFields(collectionOptions,
collectionName, dbName, false)$ ([See here](#GetEncryptedFields)).

If a set of `encryptedFields` was found, then do the following operations. If any of the following operations error, the
remaining operations are not attempted:
Expand All @@ -867,33 +867,33 @@ remaining operations are not attempted:

#### Create Encrypted Collection Helper

To support automatic generation of encryption data keys, a helper
`$CreateEncryptedCollection(CE, database, collName, collOpts, kmsProvider, masterKey)$` is defined, where `$CE$` is a
[ClientEncryption](#clientencryption-1) object, `$kmsProvider$` is a [KMSProvider](#KMSProvider) and `$masterKey$` is
equivalent to the `$masterKey$` defined in [DataKeyOpts](#datakeyopts). It has the following behavior:

- If `$collOpts$` contains an `"encryptedFields"` property, then `$EF$` is the value of that property. Otherwise, report
an error that there are no `encryptedFields` defined for the collection.
- Let `$EF'$` be a copy of `$EF$`. Update `$EF'$` in the following manner:
- Let `$Fields$` be the `"fields"` element within `$EF'$`.
- If `$Fields$` is present and is an array value, then for each element `$F$` of `Fields`:
- If `$F$` is not a document element, skip it.
- Otherwise, if `$F$` has a `"keyId"` named element `$K$` and `$K$` is a `null` value:
- Create a [DataKeyOpts](#datakeyopts) named `$dkOpts$` with the `$masterKey$` argument.
- Let `$D$` be the result of `CE.createDataKey(kmsProvider, dkOpts)`.
- If generating `$D$` resulted in an error `$E$`, the entire `$CreateEncryptedCollection$` must now fail with
error `$E$`. Return the partially-formed `$EF'$` with the error so that the caller may know what datakeys have
already been created by the helper.
- Replace `$K$` in `$F$` with `$D$`.
- Create a new set of options `$collOpts'$` duplicating `$collOpts$`. Set the `"encryptedFields"` named element of
`$collOpts'$` to `$EF'$`.
- Invoke the `CreateCollection` helper as `$CreateCollection(database, collName, collOpts')$`. Return the resulting
collection and the generated `$EF'$`. If an error occurred, return the resulting `$EF$` with the error so that the
caller may know what datakeys have already been created by the helper.

Drivers MUST document that `$createEncryptedCollection$` does not affect any auto encryption settings on existing
To support automatic generation of encryption data keys, a helper $CreateEncryptedCollection(CE, database, collName,
collOpts, kmsProvider, masterKey)$ is defined, where $CE$ is a [ClientEncryption](#clientencryption-1) object,
$kmsProvider$ is a [KMSProvider](#KMSProvider) and $masterKey$ is equivalent to the $masterKey$ defined in
[DataKeyOpts](#datakeyopts). It has the following behavior:

- If $collOpts$ contains an `"encryptedFields"` property, then $EF$ is the value of that property. Otherwise, report an
error that there are no `encryptedFields` defined for the collection.
- Let $EF'$ be a copy of $EF$. Update $EF'$ in the following manner:
- Let $Fields$ be the `"fields"` element within $EF'$.
- If $Fields$ is present and is an array value, then for each element $F$ of `Fields`:
- If $F$ is not a document element, skip it.
- Otherwise, if $F$ has a `"keyId"` named element $K$ and $K$ is a `null` value:
- Create a [DataKeyOpts](#datakeyopts) named $dkOpts$ with the $masterKey$ argument.
- Let $D$ be the result of `CE.createDataKey(kmsProvider, dkOpts)`.
- If generating $D$ resulted in an error $E$, the entire $CreateEncryptedCollection$ must now fail with error $E$.
Return the partially-formed $EF'$ with the error so that the caller may know what datakeys have already been
created by the helper.
- Replace $K$ in $F$ with $D$.
- Create a new set of options $collOpts'$ duplicating $collOpts$. Set the `"encryptedFields"` named element of
$collOpts'$ to $EF'$.
- Invoke the `CreateCollection` helper as $CreateCollection(database, collName, collOpts')$. Return the resulting
collection and the generated $EF'$. If an error occurred, return the resulting $EF$ with the error so that the caller
may know what datakeys have already been created by the helper.

Drivers MUST document that $createEncryptedCollection$ does not affect any auto encryption settings on existing
MongoClients that are already configured with auto encryption. Users must configure auto encryption after creating the
encrypted collection with the `$createEncryptedCollection$` helper.
encrypted collection with the $createEncryptedCollection$ helper.

#### Drop Collection Helper

Expand All @@ -912,9 +912,9 @@ Drivers MUST support a BSON document option named `encryptedFields` for any
>
> Drivers SHOULD NOT document the `escCollection` and `ecocCollection` options.

For a helper function `DropCollection(dropOptions)` with associated collection named `$collName$` and database named
`$dbName$`, look up the encrypted fields `encryptedFields` as
`$GetEncryptedFields(dropOptions, collName, dbname, true)$` ([See here](#GetEncryptedFields)).
For a helper function `DropCollection(dropOptions)` with associated collection named $collName$ and database named
$dbName$, look up the encrypted fields `encryptedFields` as $GetEncryptedFields(dropOptions, collName, dbname, true)$
([See here](#GetEncryptedFields)).

If a set of `encryptedFields` was found, then perform the following operations. If any of the following operations
error, the remaining operations are not attempted. A `namespace not found` error returned from the server (server error
Expand Down
Loading
Loading