@@ -76,6 +76,189 @@ func TestClientSideEncryptionProse(t *testing.T) {
7676 },
7777 }
7878
79+ runOpts := mtest .NewOptions ().MinServerVersion ("6.0" ).Topologies (mtest .ReplicaSet , mtest .LoadBalanced , mtest .ShardedReplicaSet )
80+ mt .RunOpts ("explicit encryption" , runOpts , func (mt * mtest.T ) {
81+ // Test Setup ... begin
82+ encryptedFields := readJSONFile (mt , "encrypted-fields.json" )
83+ key1Document := readJSONFile (mt , "key1-document.json" )
84+ var key1ID primitive.Binary
85+ {
86+ subtype , data := key1Document .Lookup ("_id" ).Binary ()
87+ key1ID = primitive.Binary {Subtype : subtype , Data : data }
88+ }
89+
90+ testSetup := func () (* mongo.Client , * mongo.ClientEncryption ) {
91+ mtest .DropEncryptedCollection (mt , mt .Client .Database ("db" ).Collection ("explicit_encryption" ), encryptedFields )
92+ cco := options .CreateCollection ().SetEncryptedFields (encryptedFields )
93+ err := mt .Client .Database ("db" ).CreateCollection (context .Background (), "explicit_encryption" , cco )
94+ assert .Nil (mt , err , "error on CreateCollection: %v" , err )
95+ err = mt .Client .Database ("keyvault" ).Collection ("datakeys" ).Drop (context .Background ())
96+ assert .Nil (mt , err , "error on Drop: %v" , err )
97+ keyVaultClient , err := mongo .Connect (context .TODO (), options .Client ().ApplyURI (mtest .ClusterURI ()))
98+ assert .Nil (mt , err , "error on Connect: %v" , err )
99+ datakeysColl := keyVaultClient .Database ("keyvault" ).Collection ("datakeys" , options .Collection ().SetWriteConcern (mtest .MajorityWc ))
100+ _ , err = datakeysColl .InsertOne (context .TODO (), key1Document )
101+ assert .Nil (mt , err , "error on InsertOne: %v" , err )
102+ // Create a ClientEncryption.
103+ ceo := options .ClientEncryption ().
104+ SetKeyVaultNamespace ("keyvault.datakeys" ).
105+ SetKmsProviders (fullKmsProvidersMap )
106+ clientEncryption , err := mongo .NewClientEncryption (keyVaultClient , ceo )
107+ assert .Nil (mt , err , "error on NewClientEncryption: %v" , err )
108+
109+ // Create a MongoClient with AutoEncryptionOpts and bypassQueryAnalysis=true.
110+ aeo := options .AutoEncryption ().
111+ SetKeyVaultNamespace ("keyvault.datakeys" ).
112+ SetKmsProviders (fullKmsProvidersMap ).
113+ SetBypassQueryAnalysis (true )
114+ co := options .Client ().SetAutoEncryptionOptions (aeo ).ApplyURI (mtest .ClusterURI ())
115+ encryptedClient , err := mongo .Connect (context .Background (), co )
116+ assert .Nil (mt , err , "error on Connect: %v" , err )
117+ return encryptedClient , clientEncryption
118+ }
119+ // Test Setup ... end
120+
121+ mt .Run ("case 1: can insert encrypted indexed and find" , func (mt * mtest.T ) {
122+ encryptedClient , clientEncryption := testSetup ()
123+ defer clientEncryption .Close (context .Background ())
124+ defer encryptedClient .Disconnect (context .Background ())
125+
126+ // Explicit encrypt the value "encrypted indexed value" with algorithm: "Indexed".
127+ eo := options .Encrypt ().SetAlgorithm ("Indexed" ).SetKeyID (key1ID )
128+ valueToEncrypt := "encrypted indexed value"
129+ rawVal := bson.RawValue {Type : bson .TypeString , Value : bsoncore .AppendString (nil , valueToEncrypt )}
130+ insertPayload , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
131+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
132+ // Insert.
133+ coll := encryptedClient .Database ("db" ).Collection ("explicit_encryption" )
134+ _ , err = coll .InsertOne (context .Background (), bson.D {{"_id" , 1 }, {"encryptedIndexed" , insertPayload }})
135+ assert .Nil (mt , err , "Error in InsertOne: %v" , err )
136+ // Explicit encrypt an indexed value to find.
137+ eo = options .Encrypt ().SetAlgorithm ("Indexed" ).SetKeyID (key1ID ).SetQueryType (options .QueryTypeEquality )
138+ findPayload , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
139+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
140+ // Find.
141+ res := coll .FindOne (context .Background (), bson.D {{"encryptedIndexed" , findPayload }})
142+ assert .Nil (mt , res .Err (), "Error in FindOne: %v" , res .Err ())
143+ got , err := res .DecodeBytes ()
144+ assert .Nil (mt , err , "error in DecodeBytes: %v" , err )
145+ gotValue , err := got .LookupErr ("encryptedIndexed" )
146+ assert .Nil (mt , err , "error in LookupErr: %v" , err )
147+ assert .Equal (mt , gotValue .StringValue (), valueToEncrypt , "expected %q, got %q" , valueToEncrypt , gotValue .StringValue ())
148+ })
149+ mt .Run ("case 2: can insert encrypted indexed and find with non-zero contention" , func (mt * mtest.T ) {
150+ encryptedClient , clientEncryption := testSetup ()
151+ defer clientEncryption .Close (context .Background ())
152+ defer encryptedClient .Disconnect (context .Background ())
153+
154+ coll := encryptedClient .Database ("db" ).Collection ("explicit_encryption" )
155+ valueToEncrypt := "encrypted indexed value"
156+ rawVal := bson.RawValue {Type : bson .TypeString , Value : bsoncore .AppendString (nil , valueToEncrypt )}
157+
158+ for i := 0 ; i < 10 ; i ++ {
159+ // Explicit encrypt the value "encrypted indexed value" with algorithm: "Indexed".
160+ eo := options .Encrypt ().SetAlgorithm ("Indexed" ).SetKeyID (key1ID ).SetContentionFactor (10 )
161+ insertPayload , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
162+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
163+ // Insert.
164+ _ , err = coll .InsertOne (context .Background (), bson.D {{"_id" , i }, {"encryptedIndexed" , insertPayload }})
165+ assert .Nil (mt , err , "Error in InsertOne: %v" , err )
166+ }
167+
168+ // Explicit encrypt an indexed value to find with default contentionFactor 0.
169+ {
170+ eo := options .Encrypt ().SetAlgorithm ("Indexed" ).SetKeyID (key1ID ).SetQueryType (options .QueryTypeEquality )
171+ findPayload , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
172+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
173+ // Find with contentionFactor=0.
174+ cursor , err := coll .Find (context .Background (), bson.D {{"encryptedIndexed" , findPayload }})
175+ assert .Nil (mt , err , "error in Find: %v" , err )
176+ var got []bson.Raw
177+ err = cursor .All (context .Background (), & got )
178+ assert .Nil (mt , err , "error in All: %v" , err )
179+ assert .True (mt , len (got ) < 10 , "expected len(got) < 10, got: %v" , len (got ))
180+ for _ , doc := range got {
181+ gotValue , err := doc .LookupErr ("encryptedIndexed" )
182+ assert .Nil (mt , err , "error in LookupErr: %v" , err )
183+ assert .Equal (mt , gotValue .StringValue (), valueToEncrypt , "expected %q, got %q" , valueToEncrypt , gotValue .StringValue ())
184+ }
185+ }
186+
187+ // Explicit encrypt an indexed value to find with contentionFactor 10.
188+ {
189+ eo := options .Encrypt ().SetAlgorithm ("Indexed" ).SetKeyID (key1ID ).SetQueryType (options .QueryTypeEquality ).SetContentionFactor (10 )
190+ findPayload2 , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
191+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
192+ // Find with contentionFactor=10.
193+ cursor , err := coll .Find (context .Background (), bson.D {{"encryptedIndexed" , findPayload2 }})
194+ assert .Nil (mt , err , "error in Find: %v" , err )
195+ var got []bson.Raw
196+ err = cursor .All (context .Background (), & got )
197+ assert .Nil (mt , err , "error in All: %v" , err )
198+ assert .True (mt , len (got ) == 10 , "expected len(got) == 10, got: %v" , len (got ))
199+ for _ , doc := range got {
200+ gotValue , err := doc .LookupErr ("encryptedIndexed" )
201+ assert .Nil (mt , err , "error in LookupErr: %v" , err )
202+ assert .Equal (mt , gotValue .StringValue (), valueToEncrypt , "expected %q, got %q" , valueToEncrypt , gotValue .StringValue ())
203+ }
204+ }
205+ })
206+ mt .Run ("case 3: can insert encrypted unindexed" , func (mt * mtest.T ) {
207+ encryptedClient , clientEncryption := testSetup ()
208+ defer clientEncryption .Close (context .Background ())
209+ defer encryptedClient .Disconnect (context .Background ())
210+
211+ // Explicit encrypt the value "encrypted indexed value" with algorithm: "Indexed".
212+ eo := options .Encrypt ().SetAlgorithm ("Unindexed" ).SetKeyID (key1ID )
213+ valueToEncrypt := "encrypted unindexed value"
214+ rawVal := bson.RawValue {Type : bson .TypeString , Value : bsoncore .AppendString (nil , valueToEncrypt )}
215+ insertPayload , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
216+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
217+ // Insert.
218+ coll := encryptedClient .Database ("db" ).Collection ("explicit_encryption" )
219+ _ , err = coll .InsertOne (context .Background (), bson.D {{"_id" , 1 }, {"encryptedUnindexed" , insertPayload }})
220+ assert .Nil (mt , err , "Error in InsertOne: %v" , err )
221+ // Find.
222+ res := coll .FindOne (context .Background (), bson.D {{"_id" , 1 }})
223+ assert .Nil (mt , res .Err (), "Error in FindOne: %v" , res .Err ())
224+ got , err := res .DecodeBytes ()
225+ assert .Nil (mt , err , "error in DecodeBytes: %v" , err )
226+ gotValue , err := got .LookupErr ("encryptedUnindexed" )
227+ assert .Nil (mt , err , "error in LookupErr: %v" , err )
228+ assert .Equal (mt , gotValue .StringValue (), valueToEncrypt , "expected %q, got %q" , valueToEncrypt , gotValue .StringValue ())
229+ })
230+ mt .Run ("case 4: can roundtrip encrypted indexed" , func (mt * mtest.T ) {
231+ encryptedClient , clientEncryption := testSetup ()
232+ defer clientEncryption .Close (context .Background ())
233+ defer encryptedClient .Disconnect (context .Background ())
234+
235+ // Explicit encrypt the value "encrypted indexed value" with algorithm: "Indexed".
236+ eo := options .Encrypt ().SetAlgorithm ("Indexed" ).SetKeyID (key1ID )
237+ valueToEncrypt := "encrypted indexed value"
238+ rawVal := bson.RawValue {Type : bson .TypeString , Value : bsoncore .AppendString (nil , valueToEncrypt )}
239+ payload , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
240+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
241+ gotValue , err := clientEncryption .Decrypt (context .Background (), payload )
242+ assert .Nil (mt , err , "error in Decrypt: %v" , err )
243+ assert .Equal (mt , gotValue .StringValue (), valueToEncrypt , "expected %q, got %q" , valueToEncrypt , gotValue .StringValue ())
244+ })
245+ mt .Run ("case 5: can roundtrip encrypted unindexed" , func (mt * mtest.T ) {
246+ encryptedClient , clientEncryption := testSetup ()
247+ defer clientEncryption .Close (context .Background ())
248+ defer encryptedClient .Disconnect (context .Background ())
249+
250+ // Explicit encrypt the value "encrypted indexed value" with algorithm: "Indexed".
251+ eo := options .Encrypt ().SetAlgorithm ("Unindexed" ).SetKeyID (key1ID )
252+ valueToEncrypt := "encrypted unindexed value"
253+ rawVal := bson.RawValue {Type : bson .TypeString , Value : bsoncore .AppendString (nil , valueToEncrypt )}
254+ payload , err := clientEncryption .Encrypt (context .Background (), rawVal , eo )
255+ assert .Nil (mt , err , "error in Encrypt: %v" , err )
256+ gotValue , err := clientEncryption .Decrypt (context .Background (), payload )
257+ assert .Nil (mt , err , "error in Decrypt: %v" , err )
258+ assert .Equal (mt , gotValue .StringValue (), valueToEncrypt , "expected %q, got %q" , valueToEncrypt , gotValue .StringValue ())
259+ })
260+ })
261+
79262 mt .RunOpts ("data key and double encryption" , noClientOpts , func (mt * mtest.T ) {
80263 // set up options structs
81264 schema := bson.D {
0 commit comments