github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/mongocrypt.go (about) 1 // Copyright (C) MongoDB, Inc. 2017-present. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7 //go:build cse 8 // +build cse 9 10 package mongocrypt 11 12 // #cgo linux solaris darwin pkg-config: libmongocrypt 13 // #cgo windows CFLAGS: -I"c:/libmongocrypt/include" 14 // #cgo windows LDFLAGS: -lmongocrypt -Lc:/libmongocrypt/bin 15 // #include <mongocrypt.h> 16 // #include <stdlib.h> 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" 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 // Version returns the version string for the loaded libmongocrypt, or an empty string 43 // if libmongocrypt was not loaded. 44 func Version() string { 45 str := C.GoString(C.mongocrypt_version(nil)) 46 return str 47 } 48 49 // NewMongoCrypt constructs a new MongoCrypt instance configured using the provided MongoCryptOptions. 50 func NewMongoCrypt(opts *options.MongoCryptOptions) (*MongoCrypt, error) { 51 // create mongocrypt_t handle 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 = internal.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 // set options in mongocrypt 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 // If loading the crypt_shared library isn't disabled, set the default library search path "$SYSTEM" 92 // and set a library override path if one was provided. 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 // initialize handle 108 if !C.mongocrypt_init(crypt.wrapped) { 109 return nil, crypt.createErrorFromStatus() 110 } 111 112 return crypt, nil 113 } 114 115 // CreateEncryptionContext creates a Context to use for encryption. 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 // CreateDecryptionContext creates a Context to use for decryption. 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 // lookupString returns a string for the value corresponding to the given key in the document. 150 // if the key does not exist or the value is not a string, the empty string is returned. 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 // create document {"keyAltName": keyAltName} 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 // Create document {"keyMaterial": keyMaterial} using the generic binary sybtype 0x00. 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 // CreateDataKeyContext creates a Context to use for creating a data key. 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 // Create a masterKey document of the form { "provider": <provider string>, other options... }. 207 var masterKey bsoncore.Document 208 switch { 209 case opts.MasterKey != nil: 210 // The original key passed into the top-level API was already transformed into a raw BSON document and passed 211 // down to here, so we can modify it without copying. Remove the terminating byte to add the "provider" field. 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 // createExplicitEncryptionContext creates an explicit encryption context. 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 // CreateExplicitEncryptionContext creates a Context to use for explicit encryption. 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 // CreateExplicitEncryptionExpressionContext creates a Context to use for explicit encryption of an expression. 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 // CreateExplicitDecryptionContext creates a Context to use for explicit decryption. 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 // CryptSharedLibVersion returns the version number for the loaded crypt_shared library, or 0 if the 366 // crypt_shared library was not loaded. 367 func (m *MongoCrypt) CryptSharedLibVersion() uint64 { 368 return uint64(C.mongocrypt_crypt_shared_lib_version(m.wrapped)) 369 } 370 371 // CryptSharedLibVersionString returns the version string for the loaded crypt_shared library, or an 372 // empty string if the crypt_shared library was not loaded. 373 func (m *MongoCrypt) CryptSharedLibVersionString() string { 374 // Pass in a pointer for "len", but ignore the value because C.GoString can determine the string 375 // length without it. 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 // Close cleans up any resources associated with the given MongoCrypt instance. 382 func (m *MongoCrypt) Close() { 383 C.mongocrypt_destroy(m.wrapped) 384 if m.httpClient == internal.DefaultHTTPClient { 385 internal.CloseIdleHTTPConnections(m.httpClient) 386 } 387 } 388 389 // RewrapDataKeyContext create a Context to use for rewrapping a data key. 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 // Provider is nil, but MasterKey is set. This is an error. 401 return nil, fmt.Errorf("expected 'Provider' to be set to identify type of 'MasterKey'") 402 } 403 404 if opts.Provider != nil { 405 // If a provider has been specified, create an encryption key document for creating a data key or for rewrapping 406 // datakeys. If a new provider is not specified, then the filter portion of this logic returns the data as it 407 // exists in the collection. 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 // Add new masterKey to the mongocrypt context. 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 // setLocalSchemaMap sets the local schema map in mongocrypt. 444 func (m *MongoCrypt) setLocalSchemaMap(schemaMap map[string]bsoncore.Document) error { 445 if len(schemaMap) == 0 { 446 return nil 447 } 448 449 // convert schema map to BSON document 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 // setEncryptedFieldsMap sets the encryptedfields map in mongocrypt. 465 func (m *MongoCrypt) setEncryptedFieldsMap(encryptedfieldsMap map[string]bsoncore.Document) error { 466 if len(encryptedfieldsMap) == 0 { 467 return nil 468 } 469 470 // convert encryptedfields map to BSON document 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 // createErrorFromStatus creates a new Error based on the status of the MongoCrypt instance. 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 // needsKmsProvider returns true if provider was initially set to an empty document. 494 // An empty document signals the driver to fetch credentials. 495 func needsKmsProvider(kmsProviders bsoncore.Document, provider string) bool { 496 val, err := kmsProviders.LookupErr(provider) 497 if err != nil { 498 // KMS provider is not configured. 499 return false 500 } 501 doc, ok := val.DocumentOK() 502 // KMS provider is an empty document if the length is 5. 503 // An empty document contains 4 bytes of "\x00" and a null byte. 504 return ok && len(doc) == 5 505 } 506 507 // GetKmsProviders attempts to obtain credentials from environment. 508 // It is expected to be called when a libmongocrypt context is in the mongocrypt.NeedKmsCredentials state. 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, internal.WrapErrorf(err, "unable to retrieve %s credentials", k) 515 } 516 builder.AppendDocument(k, doc) 517 } 518 return builder.Build(), nil 519 }