Skip to content

Commit 210e80a

Browse files
changes
1 parent a9ca0a4 commit 210e80a

File tree

4 files changed

+273
-25
lines changed

4 files changed

+273
-25
lines changed

src/client-side-encryption/client_encryption.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -591,14 +591,14 @@ export class ClientEncryption {
591591
field == null || typeof field !== 'object' || field.keyId != null
592592
? field
593593
: {
594-
...field,
595-
keyId: await this.createDataKey(provider, {
596-
masterKey,
597-
// clone the timeoutContext
598-
// in order to avoid sharing the same timeout for server selection and connection checkout across different concurrent operations
599-
timeoutContext: timeoutContext?.csotEnabled() ? timeoutContext?.clone() : undefined
600-
})
601-
}
594+
...field,
595+
keyId: await this.createDataKey(provider, {
596+
masterKey,
597+
// clone the timeoutContext
598+
// in order to avoid sharing the same timeout for server selection and connection checkout across different concurrent operations
599+
timeoutContext: timeoutContext?.csotEnabled() ? timeoutContext?.clone() : undefined
600+
})
601+
}
602602
);
603603
const createDataKeyResolutions = await Promise.allSettled(createDataKeyPromises);
604604

@@ -814,12 +814,12 @@ export interface ClientEncryptionEncryptOptions {
814814
* The algorithm to use for encryption.
815815
*/
816816
algorithm:
817-
| 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
818-
| 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'
819-
| 'Indexed'
820-
| 'Unindexed'
821-
| 'Range'
822-
| 'TextPreview';
817+
| 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
818+
| 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'
819+
| 'Indexed'
820+
| 'Unindexed'
821+
| 'Range'
822+
| 'TextPreview';
823823

824824
/**
825825
* The id of the Binary dataKey to use for encryption
@@ -847,7 +847,7 @@ export interface ClientEncryptionEncryptOptions {
847847

848848
interface TextQueryOptions {
849849
caseSensitive: boolean;
850-
diacraticSensitive: boolean;
850+
diacriticSensitive: boolean;
851851

852852
prefix?: {
853853
strMaxQueryLength: Int32 | number;
@@ -873,11 +873,11 @@ interface TextQueryOptions {
873873
export interface ClientEncryptionRewrapManyDataKeyProviderOptions {
874874
provider: ClientEncryptionDataKeyProvider;
875875
masterKey?:
876-
| AWSEncryptionKeyOptions
877-
| AzureEncryptionKeyOptions
878-
| GCPEncryptionKeyOptions
879-
| KMIPEncryptionKeyOptions
880-
| undefined;
876+
| AWSEncryptionKeyOptions
877+
| AzureEncryptionKeyOptions
878+
| GCPEncryptionKeyOptions
879+
| KMIPEncryptionKeyOptions
880+
| undefined;
881881
}
882882

883883
/**
@@ -1066,11 +1066,11 @@ export interface ClientEncryptionCreateDataKeyProviderOptions {
10661066
* Identifies a new KMS-specific key used to encrypt the new data key
10671067
*/
10681068
masterKey?:
1069-
| AWSEncryptionKeyOptions
1070-
| AzureEncryptionKeyOptions
1071-
| GCPEncryptionKeyOptions
1072-
| KMIPEncryptionKeyOptions
1073-
| undefined;
1069+
| AWSEncryptionKeyOptions
1070+
| AzureEncryptionKeyOptions
1071+
| GCPEncryptionKeyOptions
1072+
| KMIPEncryptionKeyOptions
1073+
| undefined;
10741074

10751075
/**
10761076
* An optional list of string alternate names used to reference a key.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import { readFile } from 'node:fs/promises';
2+
import { join } from 'node:path';
3+
4+
import { type Binary, type Document, EJSON } from 'bson';
5+
import { expect } from 'chai';
6+
7+
import { getCSFLEKMSProviders } from '../../csfle-kms-providers';
8+
import {
9+
ClientEncryption,
10+
type ClientEncryptionEncryptOptions,
11+
type MongoClient
12+
} from '../../mongodb';
13+
14+
const metadata: MongoDBMetadataUI = {
15+
requires: {
16+
clientSideEncryption: '>=6.4.0',
17+
mongodb: '>=8.2.0',
18+
topology: '!single'
19+
}
20+
};
21+
22+
const loadFLEDataFile = (filename: string) =>
23+
readFile(join(__dirname, '../../spec/client-side-encryption/etc/data', filename), {
24+
encoding: 'utf-8'
25+
});
26+
27+
describe.only('27. Text Explicit Encryption', function () {
28+
let encryptedFields: Document;
29+
let keyDocument1: Document;
30+
let keyId1: Binary;
31+
let client: MongoClient;
32+
let keyVaultClient: MongoClient;
33+
let clientEncryption: ClientEncryption;
34+
let encryptedClient: MongoClient;
35+
let encryptOpts: ClientEncryptionEncryptOptions;
36+
37+
beforeEach(async function () {
38+
encryptedFields = EJSON.parse(await loadFLEDataFile('encryptedFields-prefix-suffix.json'), {
39+
relaxed: false
40+
});
41+
keyDocument1 = EJSON.parse(await loadFLEDataFile('keys/key1-document.json'), {
42+
relaxed: false
43+
});
44+
45+
keyId1 = keyDocument1._id;
46+
client = this.configuration.newClient();
47+
48+
await client
49+
.db('db')
50+
.dropCollection('explicit_encryption', { writeConcern: { w: 'majority' }, encryptedFields });
51+
await client.db('db').createCollection('explicit_encryption', {
52+
writeConcern: { w: 'majority' },
53+
encryptedFields
54+
});
55+
56+
// Drop and create the collection `keyvault.datakeys`.
57+
// Insert `key1Document` in `keyvault.datakeys` with majority write concern.
58+
await client.db('keyvault').dropCollection('datakeys', { writeConcern: { w: 'majority' } });
59+
await client.db('keyvault').createCollection('datakeys', { writeConcern: { w: 'majority' } });
60+
await client
61+
.db('keyvault')
62+
.collection('datakeys')
63+
.insertOne(keyDocument1, { writeConcern: { w: 'majority' } });
64+
65+
// Create a MongoClient named `keyVaultClient`.
66+
keyVaultClient = this.configuration.newClient();
67+
68+
// Create a ClientEncryption object named `clientEncryption` with these options:
69+
// class ClientEncryptionOpts {
70+
// keyVaultClient: <keyVaultClient>,
71+
// keyVaultNamespace: "keyvault.datakeys",
72+
// kmsProviders: { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } },
73+
// }
74+
clientEncryption = new ClientEncryption(keyVaultClient, {
75+
keyVaultNamespace: 'keyvault.datakeys',
76+
kmsProviders: {
77+
local: getCSFLEKMSProviders().local
78+
}
79+
});
80+
81+
// Create a MongoClient named `encryptedClient` with these `AutoEncryptionOpts`:
82+
// class AutoEncryptionOpts {
83+
// keyVaultNamespace: "keyvault.datakeys",
84+
// kmsProviders: { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } },
85+
// bypassQueryAnalysis: true,
86+
// }
87+
encryptedClient = this.configuration.newClient(
88+
{},
89+
{
90+
autoEncryption: {
91+
keyVaultNamespace: 'keyvault.datakeys',
92+
kmsProviders: {
93+
local: getCSFLEKMSProviders().local
94+
},
95+
bypassQueryAnalysis: true
96+
}
97+
}
98+
);
99+
100+
encryptOpts = {
101+
keyId: keyId1,
102+
contentionFactor: 0,
103+
algorithm: 'TextPreview',
104+
textOptions: {
105+
caseSensitive: true,
106+
diacriticSensitive: true,
107+
prefix: {
108+
strMaxQueryLength: 10,
109+
strMinQueryLength: 2
110+
},
111+
suffix: {
112+
strMaxQueryLength: 10,
113+
strMinQueryLength: 2
114+
},
115+
substring: {
116+
strMaxLength: 10,
117+
strMaxQueryLength: 10,
118+
strMinQueryLength: 2
119+
}
120+
}
121+
};
122+
123+
const encryptedText = await clientEncryption.encrypt('foobarbaz', {
124+
keyId: keyId1,
125+
algorithm: 'TextPreview',
126+
contentionFactor: 0,
127+
textOptions: {
128+
caseSensitive: true,
129+
diacriticSensitive: true,
130+
prefix: {
131+
strMaxQueryLength: 10,
132+
strMinQueryLength: 2
133+
},
134+
suffix: {
135+
strMaxQueryLength: 10,
136+
strMinQueryLength: 2
137+
}
138+
}
139+
});
140+
141+
await encryptedClient
142+
.db('db')
143+
.collection<{ _id: number; encryptedText: Binary }>('explicit_encryption')
144+
.insertOne({
145+
_id: 0,
146+
encryptedText
147+
});
148+
});
149+
150+
afterEach(async function () {
151+
await Promise.allSettled([client.close(), encryptedClient.close(), keyVaultClient.close()]);
152+
});
153+
154+
it('works', async function () {
155+
const encryptedFoo = await clientEncryption.encrypt('foo', {
156+
keyId: keyId1,
157+
algorithm: 'TextPreview',
158+
contentionFactor: 0,
159+
textOptions: {
160+
caseSensitive: true,
161+
diacriticSensitive: true,
162+
prefix: {
163+
strMaxQueryLength: 10,
164+
strMinQueryLength: 2
165+
}
166+
}
167+
});
168+
169+
const filter = {
170+
$expr: { $encStrStartsWith: { input: 'encryptedText', prefix: encryptedFoo } }
171+
};
172+
173+
expect(
174+
await encryptedClient
175+
.db('db')
176+
.collection<{ _id: number; encryptedText: Binary }>('explicit_encryption')
177+
.findOne(filter)
178+
).to.deep.equal({ _id: 0, encryptedText: 'foobarbaz' });
179+
});
180+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"fields": [
3+
{
4+
"keyId": {
5+
"$binary": {
6+
"base64": "EjRWeBI0mHYSNBI0VniQEg==",
7+
"subType": "04"
8+
}
9+
},
10+
"path": "encryptedText",
11+
"bsonType": "string",
12+
"queries": [
13+
{
14+
"queryType": "prefixPreview",
15+
"strMinQueryLength": {
16+
"$numberInt": "2"
17+
},
18+
"strMaxQueryLength": {
19+
"$numberInt": "10"
20+
},
21+
"caseSensitive": true,
22+
"diacriticSensitive": true
23+
},
24+
{
25+
"queryType": "suffixPreview",
26+
"strMinQueryLength": {
27+
"$numberInt": "2"
28+
},
29+
"strMaxQueryLength": {
30+
"$numberInt": "10"
31+
},
32+
"caseSensitive": true,
33+
"diacriticSensitive": true
34+
}
35+
]
36+
}
37+
]
38+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"fields": [
3+
{
4+
"keyId": {
5+
"$binary": {
6+
"base64": "EjRWeBI0mHYSNBI0VniQEg==",
7+
"subType": "04"
8+
}
9+
},
10+
"path": "encrypted-textPreview",
11+
"bsonType": "string",
12+
"queries": [
13+
{
14+
"queryType": "substringPreview",
15+
"strMaxLength": {
16+
"$numberInt": "10"
17+
},
18+
"strMinQueryLength": {
19+
"$numberInt": "2"
20+
},
21+
"strMaxQueryLength": {
22+
"$numberInt": "10"
23+
},
24+
"caseSensitive": true,
25+
"diacriticSensitive": true
26+
}
27+
]
28+
}
29+
]
30+
}

0 commit comments

Comments
 (0)