1
2
3
4
5
6
7
8
9
10 package mongocrypt
11
12
13
14
15
16
17 import "C"
18 import (
19 "context"
20 "errors"
21 "fmt"
22 "net/http"
23 "unsafe"
24
25 "go.mongodb.org/mongo-driver/bson"
26 "go.mongodb.org/mongo-driver/internal/httputil"
27 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
28 "go.mongodb.org/mongo-driver/x/mongo/driver/auth/creds"
29 "go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/options"
30 )
31
32 type kmsProvider interface {
33 GetCredentialsDoc(context.Context) (bsoncore.Document, error)
34 }
35
36 type MongoCrypt struct {
37 wrapped *C.mongocrypt_t
38 kmsProviders map[string]kmsProvider
39 httpClient *http.Client
40 }
41
42
43
44 func Version() string {
45 str := C.GoString(C.mongocrypt_version(nil))
46 return str
47 }
48
49
50 func NewMongoCrypt(opts *options.MongoCryptOptions) (*MongoCrypt, error) {
51
52 wrapped := C.mongocrypt_new()
53 if wrapped == nil {
54 return nil, errors.New("could not create new mongocrypt object")
55 }
56 httpClient := opts.HTTPClient
57 if httpClient == nil {
58 httpClient = httputil.DefaultHTTPClient
59 }
60 kmsProviders := make(map[string]kmsProvider)
61 if needsKmsProvider(opts.KmsProviders, "gcp") {
62 kmsProviders["gcp"] = creds.NewGCPCredentialProvider(httpClient)
63 }
64 if needsKmsProvider(opts.KmsProviders, "aws") {
65 kmsProviders["aws"] = creds.NewAWSCredentialProvider(httpClient)
66 }
67 if needsKmsProvider(opts.KmsProviders, "azure") {
68 kmsProviders["azure"] = creds.NewAzureCredentialProvider(httpClient)
69 }
70 crypt := &MongoCrypt{
71 wrapped: wrapped,
72 kmsProviders: kmsProviders,
73 httpClient: httpClient,
74 }
75
76
77 if err := crypt.setProviderOptions(opts.KmsProviders); err != nil {
78 return nil, err
79 }
80 if err := crypt.setLocalSchemaMap(opts.LocalSchemaMap); err != nil {
81 return nil, err
82 }
83 if err := crypt.setEncryptedFieldsMap(opts.EncryptedFieldsMap); err != nil {
84 return nil, err
85 }
86
87 if opts.BypassQueryAnalysis {
88 C.mongocrypt_setopt_bypass_query_analysis(wrapped)
89 }
90
91
92
93 if !opts.CryptSharedLibDisabled {
94 systemStr := C.CString("$SYSTEM")
95 defer C.free(unsafe.Pointer(systemStr))
96 C.mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt.wrapped, systemStr)
97
98 if opts.CryptSharedLibOverridePath != "" {
99 cryptSharedLibOverridePathStr := C.CString(opts.CryptSharedLibOverridePath)
100 defer C.free(unsafe.Pointer(cryptSharedLibOverridePathStr))
101 C.mongocrypt_setopt_set_crypt_shared_lib_path_override(crypt.wrapped, cryptSharedLibOverridePathStr)
102 }
103 }
104
105 C.mongocrypt_setopt_use_need_kms_credentials_state(crypt.wrapped)
106
107
108 if !C.mongocrypt_init(crypt.wrapped) {
109 return nil, crypt.createErrorFromStatus()
110 }
111
112 return crypt, nil
113 }
114
115
116 func (m *MongoCrypt) CreateEncryptionContext(db string, cmd bsoncore.Document) (*Context, error) {
117 ctx := newContext(C.mongocrypt_ctx_new(m.wrapped))
118 if ctx.wrapped == nil {
119 return nil, m.createErrorFromStatus()
120 }
121
122 cmdBinary := newBinaryFromBytes(cmd)
123 defer cmdBinary.close()
124 dbStr := C.CString(db)
125 defer C.free(unsafe.Pointer(dbStr))
126
127 if ok := C.mongocrypt_ctx_encrypt_init(ctx.wrapped, dbStr, C.int32_t(-1), cmdBinary.wrapped); !ok {
128 return nil, ctx.createErrorFromStatus()
129 }
130 return ctx, nil
131 }
132
133
134 func (m *MongoCrypt) CreateDecryptionContext(cmd bsoncore.Document) (*Context, error) {
135 ctx := newContext(C.mongocrypt_ctx_new(m.wrapped))
136 if ctx.wrapped == nil {
137 return nil, m.createErrorFromStatus()
138 }
139
140 cmdBinary := newBinaryFromBytes(cmd)
141 defer cmdBinary.close()
142
143 if ok := C.mongocrypt_ctx_decrypt_init(ctx.wrapped, cmdBinary.wrapped); !ok {
144 return nil, ctx.createErrorFromStatus()
145 }
146 return ctx, nil
147 }
148
149
150
151 func lookupString(doc bsoncore.Document, key string) string {
152 strVal, _ := doc.Lookup(key).StringValueOK()
153 return strVal
154 }
155
156 func setAltName(ctx *Context, altName string) error {
157
158 idx, doc := bsoncore.AppendDocumentStart(nil)
159 doc = bsoncore.AppendStringElement(doc, "keyAltName", altName)
160 doc, _ = bsoncore.AppendDocumentEnd(doc, idx)
161
162 keyAltBinary := newBinaryFromBytes(doc)
163 defer keyAltBinary.close()
164
165 if ok := C.mongocrypt_ctx_setopt_key_alt_name(ctx.wrapped, keyAltBinary.wrapped); !ok {
166 return ctx.createErrorFromStatus()
167 }
168 return nil
169 }
170
171 func setKeyMaterial(ctx *Context, keyMaterial []byte) error {
172
173 idx, doc := bsoncore.AppendDocumentStart(nil)
174 doc = bsoncore.AppendBinaryElement(doc, "keyMaterial", 0x00, keyMaterial)
175 doc, err := bsoncore.AppendDocumentEnd(doc, idx)
176 if err != nil {
177 return err
178 }
179
180 keyMaterialBinary := newBinaryFromBytes(doc)
181 defer keyMaterialBinary.close()
182
183 if ok := C.mongocrypt_ctx_setopt_key_material(ctx.wrapped, keyMaterialBinary.wrapped); !ok {
184 return ctx.createErrorFromStatus()
185 }
186 return nil
187 }
188
189 func rewrapDataKey(ctx *Context, filter []byte) error {
190 filterBinary := newBinaryFromBytes(filter)
191 defer filterBinary.close()
192
193 if ok := C.mongocrypt_ctx_rewrap_many_datakey_init(ctx.wrapped, filterBinary.wrapped); !ok {
194 return ctx.createErrorFromStatus()
195 }
196 return nil
197 }
198
199
200 func (m *MongoCrypt) CreateDataKeyContext(kmsProvider string, opts *options.DataKeyOptions) (*Context, error) {
201 ctx := newContext(C.mongocrypt_ctx_new(m.wrapped))
202 if ctx.wrapped == nil {
203 return nil, m.createErrorFromStatus()
204 }
205
206
207 var masterKey bsoncore.Document
208 switch {
209 case opts.MasterKey != nil:
210
211
212 masterKey = opts.MasterKey[:len(opts.MasterKey)-1]
213 masterKey = bsoncore.AppendStringElement(masterKey, "provider", kmsProvider)
214 masterKey, _ = bsoncore.AppendDocumentEnd(masterKey, 0)
215 default:
216 masterKey = bsoncore.NewDocumentBuilder().AppendString("provider", kmsProvider).Build()
217 }
218
219 masterKeyBinary := newBinaryFromBytes(masterKey)
220 defer masterKeyBinary.close()
221
222 if ok := C.mongocrypt_ctx_setopt_key_encryption_key(ctx.wrapped, masterKeyBinary.wrapped); !ok {
223 return nil, ctx.createErrorFromStatus()
224 }
225
226 for _, altName := range opts.KeyAltNames {
227 if err := setAltName(ctx, altName); err != nil {
228 return nil, err
229 }
230 }
231
232 if opts.KeyMaterial != nil {
233 if err := setKeyMaterial(ctx, opts.KeyMaterial); err != nil {
234 return nil, err
235 }
236 }
237
238 if ok := C.mongocrypt_ctx_datakey_init(ctx.wrapped); !ok {
239 return nil, ctx.createErrorFromStatus()
240 }
241 return ctx, nil
242 }
243
244 const (
245 IndexTypeUnindexed = 1
246 IndexTypeIndexed = 2
247 )
248
249
250 func (m *MongoCrypt) createExplicitEncryptionContext(opts *options.ExplicitEncryptionOptions) (*Context, error) {
251 ctx := newContext(C.mongocrypt_ctx_new(m.wrapped))
252 if ctx.wrapped == nil {
253 return nil, m.createErrorFromStatus()
254 }
255
256 if opts.KeyID != nil {
257 keyIDBinary := newBinaryFromBytes(opts.KeyID.Data)
258 defer keyIDBinary.close()
259
260 if ok := C.mongocrypt_ctx_setopt_key_id(ctx.wrapped, keyIDBinary.wrapped); !ok {
261 return nil, ctx.createErrorFromStatus()
262 }
263 }
264 if opts.KeyAltName != nil {
265 if err := setAltName(ctx, *opts.KeyAltName); err != nil {
266 return nil, err
267 }
268 }
269
270 if opts.RangeOptions != nil {
271 idx, mongocryptDoc := bsoncore.AppendDocumentStart(nil)
272 if opts.RangeOptions.Min != nil {
273 mongocryptDoc = bsoncore.AppendValueElement(mongocryptDoc, "min", *opts.RangeOptions.Min)
274 }
275 if opts.RangeOptions.Max != nil {
276 mongocryptDoc = bsoncore.AppendValueElement(mongocryptDoc, "max", *opts.RangeOptions.Max)
277 }
278 if opts.RangeOptions.Precision != nil {
279 mongocryptDoc = bsoncore.AppendInt32Element(mongocryptDoc, "precision", *opts.RangeOptions.Precision)
280 }
281 mongocryptDoc = bsoncore.AppendInt64Element(mongocryptDoc, "sparsity", opts.RangeOptions.Sparsity)
282
283 mongocryptDoc, err := bsoncore.AppendDocumentEnd(mongocryptDoc, idx)
284 if err != nil {
285 return nil, err
286 }
287
288 mongocryptBinary := newBinaryFromBytes(mongocryptDoc)
289 defer mongocryptBinary.close()
290
291 if ok := C.mongocrypt_ctx_setopt_algorithm_range(ctx.wrapped, mongocryptBinary.wrapped); !ok {
292 return nil, ctx.createErrorFromStatus()
293 }
294 }
295
296 algoStr := C.CString(opts.Algorithm)
297 defer C.free(unsafe.Pointer(algoStr))
298
299 if ok := C.mongocrypt_ctx_setopt_algorithm(ctx.wrapped, algoStr, -1); !ok {
300 return nil, ctx.createErrorFromStatus()
301 }
302
303 if opts.QueryType != "" {
304 queryStr := C.CString(opts.QueryType)
305 defer C.free(unsafe.Pointer(queryStr))
306 if ok := C.mongocrypt_ctx_setopt_query_type(ctx.wrapped, queryStr, -1); !ok {
307 return nil, ctx.createErrorFromStatus()
308 }
309 }
310
311 if opts.ContentionFactor != nil {
312 if ok := C.mongocrypt_ctx_setopt_contention_factor(ctx.wrapped, C.int64_t(*opts.ContentionFactor)); !ok {
313 return nil, ctx.createErrorFromStatus()
314 }
315 }
316 return ctx, nil
317 }
318
319
320 func (m *MongoCrypt) CreateExplicitEncryptionContext(doc bsoncore.Document, opts *options.ExplicitEncryptionOptions) (*Context, error) {
321 ctx, err := m.createExplicitEncryptionContext(opts)
322 if err != nil {
323 return ctx, err
324 }
325 docBinary := newBinaryFromBytes(doc)
326 defer docBinary.close()
327 if ok := C.mongocrypt_ctx_explicit_encrypt_init(ctx.wrapped, docBinary.wrapped); !ok {
328 return nil, ctx.createErrorFromStatus()
329 }
330
331 return ctx, nil
332 }
333
334
335 func (m *MongoCrypt) CreateExplicitEncryptionExpressionContext(doc bsoncore.Document, opts *options.ExplicitEncryptionOptions) (*Context, error) {
336 ctx, err := m.createExplicitEncryptionContext(opts)
337 if err != nil {
338 return ctx, err
339 }
340 docBinary := newBinaryFromBytes(doc)
341 defer docBinary.close()
342 if ok := C.mongocrypt_ctx_explicit_encrypt_expression_init(ctx.wrapped, docBinary.wrapped); !ok {
343 return nil, ctx.createErrorFromStatus()
344 }
345
346 return ctx, nil
347 }
348
349
350 func (m *MongoCrypt) CreateExplicitDecryptionContext(doc bsoncore.Document) (*Context, error) {
351 ctx := newContext(C.mongocrypt_ctx_new(m.wrapped))
352 if ctx.wrapped == nil {
353 return nil, m.createErrorFromStatus()
354 }
355
356 docBinary := newBinaryFromBytes(doc)
357 defer docBinary.close()
358
359 if ok := C.mongocrypt_ctx_explicit_decrypt_init(ctx.wrapped, docBinary.wrapped); !ok {
360 return nil, ctx.createErrorFromStatus()
361 }
362 return ctx, nil
363 }
364
365
366
367 func (m *MongoCrypt) CryptSharedLibVersion() uint64 {
368 return uint64(C.mongocrypt_crypt_shared_lib_version(m.wrapped))
369 }
370
371
372
373 func (m *MongoCrypt) CryptSharedLibVersionString() string {
374
375
376 len := C.uint(0)
377 str := C.GoString(C.mongocrypt_crypt_shared_lib_version_string(m.wrapped, &len))
378 return str
379 }
380
381
382 func (m *MongoCrypt) Close() {
383 C.mongocrypt_destroy(m.wrapped)
384 if m.httpClient == httputil.DefaultHTTPClient {
385 httputil.CloseIdleHTTPConnections(m.httpClient)
386 }
387 }
388
389
390 func (m *MongoCrypt) RewrapDataKeyContext(filter []byte, opts *options.RewrapManyDataKeyOptions) (*Context, error) {
391 const masterKey = "masterKey"
392 const providerKey = "provider"
393
394 ctx := newContext(C.mongocrypt_ctx_new(m.wrapped))
395 if ctx.wrapped == nil {
396 return nil, m.createErrorFromStatus()
397 }
398
399 if opts.MasterKey != nil && opts.Provider == nil {
400
401 return nil, fmt.Errorf("expected 'Provider' to be set to identify type of 'MasterKey'")
402 }
403
404 if opts.Provider != nil {
405
406
407
408 idx, mongocryptDoc := bsoncore.AppendDocumentStart(nil)
409 mongocryptDoc = bsoncore.AppendStringElement(mongocryptDoc, providerKey, *opts.Provider)
410
411 if opts.MasterKey != nil {
412 mongocryptDoc = opts.MasterKey[:len(opts.MasterKey)-1]
413 mongocryptDoc = bsoncore.AppendStringElement(mongocryptDoc, providerKey, *opts.Provider)
414 }
415
416 mongocryptDoc, err := bsoncore.AppendDocumentEnd(mongocryptDoc, idx)
417 if err != nil {
418 return nil, err
419 }
420
421 mongocryptBinary := newBinaryFromBytes(mongocryptDoc)
422 defer mongocryptBinary.close()
423
424
425 if ok := C.mongocrypt_ctx_setopt_key_encryption_key(ctx.wrapped, mongocryptBinary.wrapped); !ok {
426 return nil, ctx.createErrorFromStatus()
427 }
428 }
429
430 return ctx, rewrapDataKey(ctx, filter)
431 }
432
433 func (m *MongoCrypt) setProviderOptions(kmsProviders bsoncore.Document) error {
434 providersBinary := newBinaryFromBytes(kmsProviders)
435 defer providersBinary.close()
436
437 if ok := C.mongocrypt_setopt_kms_providers(m.wrapped, providersBinary.wrapped); !ok {
438 return m.createErrorFromStatus()
439 }
440 return nil
441 }
442
443
444 func (m *MongoCrypt) setLocalSchemaMap(schemaMap map[string]bsoncore.Document) error {
445 if len(schemaMap) == 0 {
446 return nil
447 }
448
449
450 schemaMapBSON, err := bson.Marshal(schemaMap)
451 if err != nil {
452 return fmt.Errorf("error marshalling SchemaMap: %v", err)
453 }
454
455 schemaMapBinary := newBinaryFromBytes(schemaMapBSON)
456 defer schemaMapBinary.close()
457
458 if ok := C.mongocrypt_setopt_schema_map(m.wrapped, schemaMapBinary.wrapped); !ok {
459 return m.createErrorFromStatus()
460 }
461 return nil
462 }
463
464
465 func (m *MongoCrypt) setEncryptedFieldsMap(encryptedfieldsMap map[string]bsoncore.Document) error {
466 if len(encryptedfieldsMap) == 0 {
467 return nil
468 }
469
470
471 encryptedfieldsMapBSON, err := bson.Marshal(encryptedfieldsMap)
472 if err != nil {
473 return fmt.Errorf("error marshalling EncryptedFieldsMap: %v", err)
474 }
475
476 encryptedfieldsMapBinary := newBinaryFromBytes(encryptedfieldsMapBSON)
477 defer encryptedfieldsMapBinary.close()
478
479 if ok := C.mongocrypt_setopt_encrypted_field_config_map(m.wrapped, encryptedfieldsMapBinary.wrapped); !ok {
480 return m.createErrorFromStatus()
481 }
482 return nil
483 }
484
485
486 func (m *MongoCrypt) createErrorFromStatus() error {
487 status := C.mongocrypt_status_new()
488 defer C.mongocrypt_status_destroy(status)
489 C.mongocrypt_status(m.wrapped, status)
490 return errorFromStatus(status)
491 }
492
493
494
495 func needsKmsProvider(kmsProviders bsoncore.Document, provider string) bool {
496 val, err := kmsProviders.LookupErr(provider)
497 if err != nil {
498
499 return false
500 }
501 doc, ok := val.DocumentOK()
502
503
504 return ok && len(doc) == 5
505 }
506
507
508
509 func (m *MongoCrypt) GetKmsProviders(ctx context.Context) (bsoncore.Document, error) {
510 builder := bsoncore.NewDocumentBuilder()
511 for k, p := range m.kmsProviders {
512 doc, err := p.GetCredentialsDoc(ctx)
513 if err != nil {
514 return nil, fmt.Errorf("unable to retrieve %s credentials: %w", k, err)
515 }
516 builder.AppendDocument(k, doc)
517 }
518 return builder.Build(), nil
519 }
520
View as plain text