Skip to content

GODRIVER-1431 Add explicit encryption examples #350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 190 additions & 2 deletions mongo/client_side_encryption_examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"log"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)

Expand Down Expand Up @@ -131,10 +132,197 @@ func Example_clientSideEncryptionCreateKey() {
if err != nil {
log.Fatalf("Connect error for encrypted client: %v", err)
}
defer func() {
_ = client.Disconnect(context.TODO())
}()

// Use client for operations.
}

func Example_explictEncryption() {
var localMasterKey []byte // This must be the same master key that was used to create the encryption key.
kmsProviders := map[string]map[string]interface{}{
"local": {
"key": localMasterKey,
},
}

// The MongoDB namespace (db.collection) used to store the encryption data keys.
keyVaultDBName, keyVaultCollName := "encryption", "testKeyVault"
keyVaultNamespace := keyVaultDBName + "." + keyVaultCollName

// The Client used to read/write application data.
client, err := Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
panic(err)
}
defer func() { _ = client.Disconnect(context.TODO()) }()

// Get a handle to the application collection and clear existing data.
coll := client.Database("test").Collection("coll")
_ = coll.Drop(context.TODO())

// Set up the key vault for this example.
keyVaultColl := client.Database(keyVaultDBName).Collection(keyVaultCollName)
_ = keyVaultColl.Drop(context.TODO())
// Ensure that two data keys cannot share the same keyAltName.
keyVaultIndex := IndexModel{
Keys: bson.D{{"keyAltNames", 1}},
Options: options.Index().
SetUnique(true).
SetPartialFilterExpression(bson.D{
{"keyAltNames", bson.D{
{"$exists", true},
}},
}),
}
if _, err = keyVaultColl.Indexes().CreateOne(context.TODO(), keyVaultIndex); err != nil {
panic(err)
}

// Create the ClientEncryption object to use for explicit encryption/decryption. The Client passed to
// NewClientEncryption is used to read/write to the key vault. This can be the same Client used by the main
// application.
clientEncryptionOpts := options.ClientEncryption().
SetKmsProviders(kmsProviders).
SetKeyVaultNamespace(keyVaultNamespace)
clientEncryption, err := NewClientEncryption(client, clientEncryptionOpts)
if err != nil {
panic(err)
}
defer func() { _ = clientEncryption.Close(context.TODO()) }()

// Create a new data key for the encrypted field.
dataKeyOpts := options.DataKey().SetKeyAltNames([]string{"go_encryption_example"})
dataKeyID, err := clientEncryption.CreateDataKey(context.TODO(), "local", dataKeyOpts)
if err != nil {
panic(err)
}

// Create a bson.RawValue to encrypt and encrypt it using the key that was just created.
rawValueType, rawValueData, err := bson.MarshalValue("123456789")
if err != nil {
panic(err)
}
rawValue := bson.RawValue{Type: rawValueType, Value: rawValueData}
encryptionOpts := options.Encrypt().
SetAlgorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").
SetKeyID(dataKeyID)
encryptedField, err := clientEncryption.Encrypt(context.TODO(), rawValue, encryptionOpts)
if err != nil {
panic(err)
}

// Insert a document with the encrypted field and then find it.
if _, err = coll.InsertOne(context.TODO(), bson.D{{"encryptedField", encryptedField}}); err != nil {
panic(err)
}
var foundDoc bson.M
if err = coll.FindOne(context.TODO(), bson.D{}).Decode(&foundDoc); err != nil {
panic(err)
}

// Decrypt the encrypted field in the found document.
decrypted, err := clientEncryption.Decrypt(context.TODO(), foundDoc["encryptedField"].(primitive.Binary))
if err != nil {
panic(err)
}
fmt.Printf("Decrypted value: %s\n", decrypted)
}

func Example_explictEncryptionWithAutomaticDecryption() {
// Automatic encryption requires MongoDB 4.2 enterprise, but automatic decryption is supported for all users.

var localMasterKey []byte // This must be the same master key that was used to create the encryption key.
kmsProviders := map[string]map[string]interface{}{
"local": {
"key": localMasterKey,
},
}

// The MongoDB namespace (db.collection) used to store the encryption data keys.
keyVaultDBName, keyVaultCollName := "encryption", "testKeyVault"
keyVaultNamespace := keyVaultDBName + "." + keyVaultCollName

// Create the Client for reading/writing application data. Configure it with BypassAutoEncryption=true to disable
// automatic encryption but keep automatic decryption. Setting BypassAutoEncryption will also bypass spawning
// mongocryptd in the driver.
autoEncryptionOpts := options.AutoEncryption().
SetKmsProviders(kmsProviders).
SetKeyVaultNamespace(keyVaultNamespace).
SetBypassAutoEncryption(true)
clientOpts := options.Client().
ApplyURI("mongodb://localhost:27017").
SetAutoEncryptionOptions(autoEncryptionOpts)
client, err := Connect(context.TODO(), clientOpts)
if err != nil {
panic(err)
}
defer func() { _ = client.Disconnect(context.TODO()) }()

// Get a handle to the application collection and clear existing data.
coll := client.Database("test").Collection("coll")
_ = coll.Drop(context.TODO())

if err = client.Disconnect(context.TODO()); err != nil {
log.Fatalf("Disconnect error: %v", err)
// Set up the key vault for this example.
keyVaultColl := client.Database(keyVaultDBName).Collection(keyVaultCollName)
_ = keyVaultColl.Drop(context.TODO())
// Ensure that two data keys cannot share the same keyAltName.
keyVaultIndex := IndexModel{
Keys: bson.D{{"keyAltNames", 1}},
Options: options.Index().
SetUnique(true).
SetPartialFilterExpression(bson.D{
{"keyAltNames", bson.D{
{"$exists", true},
}},
}),
}
if _, err = keyVaultColl.Indexes().CreateOne(context.TODO(), keyVaultIndex); err != nil {
panic(err)
}

// Create the ClientEncryption object to use for explicit encryption/decryption. The Client passed to
// NewClientEncryption is used to read/write to the key vault. This can be the same Client used by the main
// application.
clientEncryptionOpts := options.ClientEncryption().
SetKmsProviders(kmsProviders).
SetKeyVaultNamespace(keyVaultNamespace)
clientEncryption, err := NewClientEncryption(client, clientEncryptionOpts)
if err != nil {
panic(err)
}
defer func() { _ = clientEncryption.Close(context.TODO()) }()

// Create a new data key for the encrypted field.
dataKeyOpts := options.DataKey().SetKeyAltNames([]string{"go_encryption_example"})
dataKeyID, err := clientEncryption.CreateDataKey(context.TODO(), "local", dataKeyOpts)
if err != nil {
panic(err)
}

// Create a bson.RawValue to encrypt and encrypt it using the key that was just created.
rawValueType, rawValueData, err := bson.MarshalValue("123456789")
if err != nil {
panic(err)
}
rawValue := bson.RawValue{Type: rawValueType, Value: rawValueData}
encryptionOpts := options.Encrypt().
SetAlgorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").
SetKeyID(dataKeyID)
encryptedField, err := clientEncryption.Encrypt(context.TODO(), rawValue, encryptionOpts)
if err != nil {
panic(err)
}

// Insert a document with the encrypted field and then find it. The FindOne call will automatically decrypt the
// field in the document.
if _, err = coll.InsertOne(context.TODO(), bson.D{{"encryptedField", encryptedField}}); err != nil {
panic(err)
}
var foundDoc bson.M
if err = coll.FindOne(context.TODO(), bson.D{}).Decode(&foundDoc); err != nil {
panic(err)
}
fmt.Printf("Decrypted document: %v\n", foundDoc)
}