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
7package mongo
8
9import (
10	"context"
11	"crypto/rand"
12	"encoding/base64"
13	"fmt"
14	"log"
15
16	"go.mongodb.org/mongo-driver/bson"
17	"go.mongodb.org/mongo-driver/mongo/options"
18)
19
20func Example_clientSideEncryption() {
21	// This would have to be the same master key that was used to create the encryption key
22	localKey := make([]byte, 96)
23	if _, err := rand.Read(localKey); err != nil {
24		log.Fatal(err)
25	}
26	kmsProviders := map[string]map[string]interface{}{
27		"local": {
28			"key": localKey,
29		},
30	}
31	keyVaultNamespace := "admin.datakeys"
32
33	uri := "mongodb://localhost:27017"
34	autoEncryptionOpts := options.AutoEncryption().
35		SetKeyVaultNamespace(keyVaultNamespace).
36		SetKmsProviders(kmsProviders)
37	clientOpts := options.Client().ApplyURI(uri).SetAutoEncryptionOptions(autoEncryptionOpts)
38	client, err := Connect(context.TODO(), clientOpts)
39	if err != nil {
40		log.Fatalf("Connect error: %v", err)
41	}
42	defer func() {
43		if err = client.Disconnect(context.TODO()); err != nil {
44			log.Fatalf("Disconnect error: %v", err)
45		}
46	}()
47
48	collection := client.Database("test").Collection("coll")
49	if err := collection.Drop(context.TODO()); err != nil {
50		log.Fatalf("Collection.Drop error: %v", err)
51	}
52
53	if _, err = collection.InsertOne(context.TODO(), bson.D{{"encryptedField", "123456789"}}); err != nil {
54		log.Fatalf("InsertOne error: %v", err)
55	}
56	res, err := collection.FindOne(context.TODO(), bson.D{}).DecodeBytes()
57	if err != nil {
58		log.Fatalf("FindOne error: %v", err)
59	}
60	fmt.Println(res)
61}
62
63func Example_clientSideEncryptionCreateKey() {
64	keyVaultNamespace := "admin.datakeys"
65	uri := "mongodb://localhost:27017"
66	// kmsProviders would have to be populated with the correct KMS provider information before it's used
67	var kmsProviders map[string]map[string]interface{}
68
69	// Create Client and ClientEncryption
70	clientEncryptionOpts := options.ClientEncryption().
71		SetKeyVaultNamespace(keyVaultNamespace).
72		SetKmsProviders(kmsProviders)
73	keyVaultClient, err := Connect(context.TODO(), options.Client().ApplyURI(uri))
74	if err != nil {
75		log.Fatalf("Connect error for keyVaultClient: %v", err)
76	}
77	clientEnc, err := NewClientEncryption(keyVaultClient, clientEncryptionOpts)
78	if err != nil {
79		log.Fatalf("NewClientEncryption error: %v", err)
80	}
81	defer func() {
82		// this will disconnect the keyVaultClient as well
83		if err = clientEnc.Close(context.TODO()); err != nil {
84			log.Fatalf("Close error: %v", err)
85		}
86	}()
87
88	// Create a new data key and encode it as base64
89	dataKeyID, err := clientEnc.CreateDataKey(context.TODO(), "local")
90	if err != nil {
91		log.Fatalf("CreateDataKey error: %v", err)
92	}
93	dataKeyBase64 := base64.StdEncoding.EncodeToString(dataKeyID.Data)
94
95	// Create a JSON schema using the new data key. This schema could also be written in a separate file and read in
96	// using I/O functions.
97	schema := `{
98		"properties": {
99			"encryptedField": {
100				"encrypt": {
101					"keyId": [{
102						"$binary": {
103							"base64": "%s",
104							"subType": "04"
105						}
106					}],
107					"bsonType": "string",
108					"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
109				}
110			}
111		},
112		"bsonType": "object"
113	}`
114	schema = fmt.Sprintf(schema, dataKeyBase64)
115	var schemaDoc bson.Raw
116	if err = bson.UnmarshalExtJSON([]byte(schema), true, &schemaDoc); err != nil {
117		log.Fatalf("UnmarshalExtJSON error: %v", err)
118	}
119
120	// Configure a Client with auto encryption using the new schema
121	dbName := "test"
122	collName := "coll"
123	schemaMap := map[string]interface{}{
124		dbName + "." + collName: schemaDoc,
125	}
126	autoEncryptionOpts := options.AutoEncryption().
127		SetKmsProviders(kmsProviders).
128		SetKeyVaultNamespace(keyVaultNamespace).
129		SetSchemaMap(schemaMap)
130	client, err := Connect(context.TODO(), options.Client().ApplyURI(uri).SetAutoEncryptionOptions(autoEncryptionOpts))
131	if err != nil {
132		log.Fatalf("Connect error for encrypted client: %v", err)
133	}
134
135	// Use client for operations.
136
137	if err = client.Disconnect(context.TODO()); err != nil {
138		log.Fatalf("Disconnect error: %v", err)
139	}
140}
141