github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/mongo/client_encryption.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 package mongo 8 9 import ( 10 "context" 11 "errors" 12 "fmt" 13 "strings" 14 15 "go.mongodb.org/mongo-driver/bson" 16 "go.mongodb.org/mongo-driver/bson/bsonrw" 17 "go.mongodb.org/mongo-driver/bson/primitive" 18 "go.mongodb.org/mongo-driver/mongo/options" 19 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" 20 "go.mongodb.org/mongo-driver/x/mongo/driver" 21 "go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt" 22 mcopts "go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/options" 23 ) 24 25 // ClientEncryption is used to create data keys and explicitly encrypt and decrypt BSON values. 26 type ClientEncryption struct { 27 crypt driver.Crypt 28 keyVaultClient *Client 29 keyVaultColl *Collection 30 } 31 32 // NewClientEncryption creates a new ClientEncryption instance configured with the given options. 33 func NewClientEncryption(keyVaultClient *Client, opts ...*options.ClientEncryptionOptions) (*ClientEncryption, error) { 34 if keyVaultClient == nil { 35 return nil, errors.New("keyVaultClient must not be nil") 36 } 37 38 ce := &ClientEncryption{ 39 keyVaultClient: keyVaultClient, 40 } 41 ceo := options.MergeClientEncryptionOptions(opts...) 42 43 // create keyVaultColl 44 db, coll := splitNamespace(ceo.KeyVaultNamespace) 45 ce.keyVaultColl = ce.keyVaultClient.Database(db).Collection(coll, keyVaultCollOpts) 46 47 kmsProviders, err := marshal(ceo.KmsProviders, nil, nil) 48 if err != nil { 49 return nil, fmt.Errorf("error creating KMS providers map: %v", err) 50 } 51 52 mc, err := mongocrypt.NewMongoCrypt(mcopts.MongoCrypt(). 53 SetKmsProviders(kmsProviders). 54 // Explicitly disable loading the crypt_shared library for the Crypt used for 55 // ClientEncryption because it's only needed for AutoEncryption and we don't expect users to 56 // have the crypt_shared library installed if they're using ClientEncryption. 57 SetCryptSharedLibDisabled(true). 58 SetHTTPClient(ceo.HTTPClient)) 59 if err != nil { 60 return nil, err 61 } 62 63 // create Crypt 64 kr := keyRetriever{coll: ce.keyVaultColl} 65 cir := collInfoRetriever{client: ce.keyVaultClient} 66 ce.crypt = driver.NewCrypt(&driver.CryptOptions{ 67 MongoCrypt: mc, 68 KeyFn: kr.cryptKeys, 69 CollInfoFn: cir.cryptCollInfo, 70 TLSConfig: ceo.TLSConfig, 71 }) 72 73 return ce, nil 74 } 75 76 // CreateEncryptedCollection creates a new collection for Queryable Encryption with the help of automatic generation of new encryption data keys for null keyIds. 77 // It returns the created collection and the encrypted fields document used to create it. 78 func (ce *ClientEncryption) CreateEncryptedCollection(ctx context.Context, 79 db *Database, coll string, createOpts *options.CreateCollectionOptions, 80 kmsProvider string, masterKey interface{}) (*Collection, bson.M, error) { 81 if createOpts == nil { 82 return nil, nil, errors.New("nil CreateCollectionOptions") 83 } 84 ef := createOpts.EncryptedFields 85 if ef == nil { 86 return nil, nil, errors.New("no EncryptedFields defined for the collection") 87 } 88 89 efBSON, err := marshal(ef, db.bsonOpts, db.registry) 90 if err != nil { 91 return nil, nil, err 92 } 93 r := bsonrw.NewBSONDocumentReader(efBSON) 94 dec, err := bson.NewDecoder(r) 95 if err != nil { 96 return nil, nil, err 97 } 98 var m bson.M 99 err = dec.Decode(&m) 100 if err != nil { 101 return nil, nil, err 102 } 103 104 if v, ok := m["fields"]; ok { 105 if fields, ok := v.(bson.A); ok { 106 for _, field := range fields { 107 if f, ok := field.(bson.M); !ok { 108 continue 109 } else if v, ok := f["keyId"]; ok && v == nil { 110 dkOpts := options.DataKey() 111 if masterKey != nil { 112 dkOpts.SetMasterKey(masterKey) 113 } 114 keyid, err := ce.CreateDataKey(ctx, kmsProvider, dkOpts) 115 if err != nil { 116 createOpts.EncryptedFields = m 117 return nil, m, err 118 } 119 f["keyId"] = keyid 120 } 121 } 122 createOpts.EncryptedFields = m 123 } 124 } 125 err = db.CreateCollection(ctx, coll, createOpts) 126 if err != nil { 127 return nil, m, err 128 } 129 return db.Collection(coll), m, nil 130 } 131 132 // AddKeyAltName adds a keyAltName to the keyAltNames array of the key document in the key vault collection with the 133 // given UUID (BSON binary subtype 0x04). Returns the previous version of the key document. 134 func (ce *ClientEncryption) AddKeyAltName(ctx context.Context, id primitive.Binary, keyAltName string) *SingleResult { 135 filter := bsoncore.NewDocumentBuilder().AppendBinary("_id", id.Subtype, id.Data).Build() 136 keyAltNameDoc := bsoncore.NewDocumentBuilder().AppendString("keyAltNames", keyAltName).Build() 137 update := bsoncore.NewDocumentBuilder().AppendDocument("$addToSet", keyAltNameDoc).Build() 138 return ce.keyVaultColl.FindOneAndUpdate(ctx, filter, update) 139 } 140 141 // CreateDataKey creates a new key document and inserts into the key vault collection. Returns the _id of the created 142 // document as a UUID (BSON binary subtype 0x04). 143 func (ce *ClientEncryption) CreateDataKey(ctx context.Context, kmsProvider string, 144 opts ...*options.DataKeyOptions) (primitive.Binary, error) { 145 146 // translate opts to mcopts.DataKeyOptions 147 dko := options.MergeDataKeyOptions(opts...) 148 co := mcopts.DataKey().SetKeyAltNames(dko.KeyAltNames) 149 if dko.MasterKey != nil { 150 keyDoc, err := marshal( 151 dko.MasterKey, 152 ce.keyVaultClient.bsonOpts, 153 ce.keyVaultClient.registry) 154 if err != nil { 155 return primitive.Binary{}, err 156 } 157 co.SetMasterKey(keyDoc) 158 } 159 if dko.KeyMaterial != nil { 160 co.SetKeyMaterial(dko.KeyMaterial) 161 } 162 163 // create data key document 164 dataKeyDoc, err := ce.crypt.CreateDataKey(ctx, kmsProvider, co) 165 if err != nil { 166 return primitive.Binary{}, err 167 } 168 169 // insert key into key vault 170 _, err = ce.keyVaultColl.InsertOne(ctx, dataKeyDoc) 171 if err != nil { 172 return primitive.Binary{}, err 173 } 174 175 subtype, data := bson.Raw(dataKeyDoc).Lookup("_id").Binary() 176 return primitive.Binary{Subtype: subtype, Data: data}, nil 177 } 178 179 // transformExplicitEncryptionOptions creates explicit encryption options to be passed to libmongocrypt. 180 func transformExplicitEncryptionOptions(opts ...*options.EncryptOptions) *mcopts.ExplicitEncryptionOptions { 181 eo := options.MergeEncryptOptions(opts...) 182 transformed := mcopts.ExplicitEncryption() 183 if eo.KeyID != nil { 184 transformed.SetKeyID(*eo.KeyID) 185 } 186 if eo.KeyAltName != nil { 187 transformed.SetKeyAltName(*eo.KeyAltName) 188 } 189 transformed.SetAlgorithm(eo.Algorithm) 190 transformed.SetQueryType(eo.QueryType) 191 192 if eo.ContentionFactor != nil { 193 transformed.SetContentionFactor(*eo.ContentionFactor) 194 } 195 196 if eo.RangeOptions != nil { 197 var transformedRange mcopts.ExplicitRangeOptions 198 if eo.RangeOptions.Min != nil { 199 transformedRange.Min = &bsoncore.Value{Type: eo.RangeOptions.Min.Type, Data: eo.RangeOptions.Min.Value} 200 } 201 if eo.RangeOptions.Max != nil { 202 transformedRange.Max = &bsoncore.Value{Type: eo.RangeOptions.Max.Type, Data: eo.RangeOptions.Max.Value} 203 } 204 if eo.RangeOptions.Precision != nil { 205 transformedRange.Precision = eo.RangeOptions.Precision 206 } 207 transformedRange.Sparsity = eo.RangeOptions.Sparsity 208 transformed.SetRangeOptions(transformedRange) 209 } 210 return transformed 211 } 212 213 // Encrypt encrypts a BSON value with the given key and algorithm. Returns an encrypted value (BSON binary of subtype 6). 214 func (ce *ClientEncryption) Encrypt(ctx context.Context, val bson.RawValue, 215 opts ...*options.EncryptOptions) (primitive.Binary, error) { 216 217 transformed := transformExplicitEncryptionOptions(opts...) 218 subtype, data, err := ce.crypt.EncryptExplicit(ctx, bsoncore.Value{Type: val.Type, Data: val.Value}, transformed) 219 if err != nil { 220 return primitive.Binary{}, err 221 } 222 return primitive.Binary{Subtype: subtype, Data: data}, nil 223 } 224 225 // EncryptExpression encrypts an expression to query a range index. 226 // On success, `result` is populated with the resulting BSON document. 227 // `expr` is expected to be a BSON document of one of the following forms: 228 // 1. A Match Expression of this form: 229 // {$and: [{<field>: {$gt: <value1>}}, {<field>: {$lt: <value2> }}]} 230 // 2. An Aggregate Expression of this form: 231 // {$and: [{$gt: [<fieldpath>, <value1>]}, {$lt: [<fieldpath>, <value2>]}] 232 // $gt may also be $gte. $lt may also be $lte. 233 // Only supported for queryType "rangePreview" 234 // Beta: The Range algorithm is experimental only. It is not intended for public use. It is subject to breaking changes. 235 func (ce *ClientEncryption) EncryptExpression(ctx context.Context, expr interface{}, result interface{}, opts ...*options.EncryptOptions) error { 236 transformed := transformExplicitEncryptionOptions(opts...) 237 238 exprDoc, err := marshal(expr, nil, nil) 239 if err != nil { 240 return err 241 } 242 243 encryptedExprDoc, err := ce.crypt.EncryptExplicitExpression(ctx, exprDoc, transformed) 244 if err != nil { 245 return err 246 } 247 if raw, ok := result.(*bson.Raw); ok { 248 // Avoid the cost of Unmarshal. 249 *raw = bson.Raw(encryptedExprDoc) 250 return nil 251 } 252 err = bson.Unmarshal([]byte(encryptedExprDoc), result) 253 if err != nil { 254 return err 255 } 256 return nil 257 } 258 259 // Decrypt decrypts an encrypted value (BSON binary of subtype 6) and returns the original BSON value. 260 func (ce *ClientEncryption) Decrypt(ctx context.Context, val primitive.Binary) (bson.RawValue, error) { 261 decrypted, err := ce.crypt.DecryptExplicit(ctx, val.Subtype, val.Data) 262 if err != nil { 263 return bson.RawValue{}, err 264 } 265 266 return bson.RawValue{Type: decrypted.Type, Value: decrypted.Data}, nil 267 } 268 269 // Close cleans up any resources associated with the ClientEncryption instance. This includes disconnecting the 270 // key-vault Client instance. 271 func (ce *ClientEncryption) Close(ctx context.Context) error { 272 ce.crypt.Close() 273 return ce.keyVaultClient.Disconnect(ctx) 274 } 275 276 // DeleteKey removes the key document with the given UUID (BSON binary subtype 0x04) from the key vault collection. 277 // Returns the result of the internal deleteOne() operation on the key vault collection. 278 func (ce *ClientEncryption) DeleteKey(ctx context.Context, id primitive.Binary) (*DeleteResult, error) { 279 filter := bsoncore.NewDocumentBuilder().AppendBinary("_id", id.Subtype, id.Data).Build() 280 return ce.keyVaultColl.DeleteOne(ctx, filter) 281 } 282 283 // GetKeyByAltName returns a key document in the key vault collection with the given keyAltName. 284 func (ce *ClientEncryption) GetKeyByAltName(ctx context.Context, keyAltName string) *SingleResult { 285 filter := bsoncore.NewDocumentBuilder().AppendString("keyAltNames", keyAltName).Build() 286 return ce.keyVaultColl.FindOne(ctx, filter) 287 } 288 289 // GetKey finds a single key document with the given UUID (BSON binary subtype 0x04). Returns the result of the 290 // internal find() operation on the key vault collection. 291 func (ce *ClientEncryption) GetKey(ctx context.Context, id primitive.Binary) *SingleResult { 292 filter := bsoncore.NewDocumentBuilder().AppendBinary("_id", id.Subtype, id.Data).Build() 293 return ce.keyVaultColl.FindOne(ctx, filter) 294 } 295 296 // GetKeys finds all documents in the key vault collection. Returns the result of the internal find() operation on the 297 // key vault collection. 298 func (ce *ClientEncryption) GetKeys(ctx context.Context) (*Cursor, error) { 299 return ce.keyVaultColl.Find(ctx, bson.D{}) 300 } 301 302 // RemoveKeyAltName removes a keyAltName from the keyAltNames array of the key document in the key vault collection with 303 // the given UUID (BSON binary subtype 0x04). Returns the previous version of the key document. 304 func (ce *ClientEncryption) RemoveKeyAltName(ctx context.Context, id primitive.Binary, keyAltName string) *SingleResult { 305 filter := bsoncore.NewDocumentBuilder().AppendBinary("_id", id.Subtype, id.Data).Build() 306 update := bson.A{bson.D{{"$set", bson.D{{"keyAltNames", bson.D{{"$cond", bson.A{bson.D{{"$eq", 307 bson.A{"$keyAltNames", bson.A{keyAltName}}}}, "$$REMOVE", bson.D{{"$filter", 308 bson.D{{"input", "$keyAltNames"}, {"cond", bson.D{{"$ne", bson.A{"$$this", keyAltName}}}}}}}}}}}}}}} 309 return ce.keyVaultColl.FindOneAndUpdate(ctx, filter, update) 310 } 311 312 // setRewrapManyDataKeyWriteModels will prepare the WriteModel slice for a bulk updating rewrapped documents. 313 func setRewrapManyDataKeyWriteModels(rewrappedDocuments []bsoncore.Document, writeModels *[]WriteModel) error { 314 const idKey = "_id" 315 const keyMaterial = "keyMaterial" 316 const masterKey = "masterKey" 317 318 if writeModels == nil { 319 return fmt.Errorf("writeModels pointer not set for location referenced") 320 } 321 322 // Append a slice of WriteModel with the update document per each rewrappedDoc _id filter. 323 for _, rewrappedDocument := range rewrappedDocuments { 324 // Prepare the new master key for update. 325 masterKeyValue, err := rewrappedDocument.LookupErr(masterKey) 326 if err != nil { 327 return err 328 } 329 masterKeyDoc := masterKeyValue.Document() 330 331 // Prepare the new material key for update. 332 keyMaterialValue, err := rewrappedDocument.LookupErr(keyMaterial) 333 if err != nil { 334 return err 335 } 336 keyMaterialSubtype, keyMaterialData := keyMaterialValue.Binary() 337 keyMaterialBinary := primitive.Binary{Subtype: keyMaterialSubtype, Data: keyMaterialData} 338 339 // Prepare the _id filter for documents to update. 340 id, err := rewrappedDocument.LookupErr(idKey) 341 if err != nil { 342 return err 343 } 344 345 idSubtype, idData, ok := id.BinaryOK() 346 if !ok { 347 return fmt.Errorf("expected to assert %q as binary, got type %T", idKey, id) 348 } 349 binaryID := primitive.Binary{Subtype: idSubtype, Data: idData} 350 351 // Append the mutable document to the slice for bulk update. 352 *writeModels = append(*writeModels, NewUpdateOneModel(). 353 SetFilter(bson.D{{idKey, binaryID}}). 354 SetUpdate( 355 bson.D{ 356 {"$set", bson.D{{keyMaterial, keyMaterialBinary}, {masterKey, masterKeyDoc}}}, 357 {"$currentDate", bson.D{{"updateDate", true}}}, 358 }, 359 )) 360 } 361 return nil 362 } 363 364 // RewrapManyDataKey decrypts and encrypts all matching data keys with a possibly new masterKey value. For all 365 // matching documents, this method will overwrite the "masterKey", "updateDate", and "keyMaterial". On error, some 366 // matching data keys may have been rewrapped. 367 // libmongocrypt 1.5.2 is required. An error is returned if the detected version of libmongocrypt is less than 1.5.2. 368 func (ce *ClientEncryption) RewrapManyDataKey(ctx context.Context, filter interface{}, 369 opts ...*options.RewrapManyDataKeyOptions) (*RewrapManyDataKeyResult, error) { 370 371 // libmongocrypt versions 1.5.0 and 1.5.1 have a severe bug in RewrapManyDataKey. 372 // Check if the version string starts with 1.5.0 or 1.5.1. This accounts for pre-release versions, like 1.5.0-rc0. 373 libmongocryptVersion := mongocrypt.Version() 374 if strings.HasPrefix(libmongocryptVersion, "1.5.0") || strings.HasPrefix(libmongocryptVersion, "1.5.1") { 375 return nil, fmt.Errorf("RewrapManyDataKey requires libmongocrypt 1.5.2 or newer. Detected version: %v", libmongocryptVersion) 376 } 377 378 rmdko := options.MergeRewrapManyDataKeyOptions(opts...) 379 if ctx == nil { 380 ctx = context.Background() 381 } 382 383 // Transfer rmdko options to /x/ package options to publish the mongocrypt feed. 384 co := mcopts.RewrapManyDataKey() 385 if rmdko.MasterKey != nil { 386 keyDoc, err := marshal( 387 rmdko.MasterKey, 388 ce.keyVaultClient.bsonOpts, 389 ce.keyVaultClient.registry) 390 if err != nil { 391 return nil, err 392 } 393 co.SetMasterKey(keyDoc) 394 } 395 if rmdko.Provider != nil { 396 co.SetProvider(*rmdko.Provider) 397 } 398 399 // Prepare the filters and rewrap the data key using mongocrypt. 400 filterdoc, err := marshal(filter, ce.keyVaultClient.bsonOpts, ce.keyVaultClient.registry) 401 if err != nil { 402 return nil, err 403 } 404 405 rewrappedDocuments, err := ce.crypt.RewrapDataKey(ctx, filterdoc, co) 406 if err != nil { 407 return nil, err 408 } 409 if len(rewrappedDocuments) == 0 { 410 // If there are no documents to rewrap, then do nothing. 411 return new(RewrapManyDataKeyResult), nil 412 } 413 414 // Prepare the WriteModel slice for bulk updating the rewrapped data keys. 415 models := []WriteModel{} 416 if err := setRewrapManyDataKeyWriteModels(rewrappedDocuments, &models); err != nil { 417 return nil, err 418 } 419 420 bulkWriteResults, err := ce.keyVaultColl.BulkWrite(ctx, models) 421 return &RewrapManyDataKeyResult{BulkWriteResult: bulkWriteResults}, err 422 } 423 424 // splitNamespace takes a namespace in the form "database.collection" and returns (database name, collection name) 425 func splitNamespace(ns string) (string, string) { 426 firstDot := strings.Index(ns, ".") 427 if firstDot == -1 { 428 return "", ns 429 } 430 431 return ns[:firstDot], ns[firstDot+1:] 432 }