1package keyservice
2
3import (
4	"fmt"
5
6	"go.mozilla.org/sops/v3/azkv"
7	"go.mozilla.org/sops/v3/gcpkms"
8	"go.mozilla.org/sops/v3/hcvault"
9	"go.mozilla.org/sops/v3/kms"
10	"go.mozilla.org/sops/v3/pgp"
11	"golang.org/x/net/context"
12	"google.golang.org/grpc"
13	"google.golang.org/grpc/codes"
14	"google.golang.org/grpc/status"
15)
16
17// Server is a key service server that uses SOPS MasterKeys to fulfill requests
18type Server struct {
19	// Prompt indicates whether the server should prompt before decrypting or encrypting data
20	Prompt bool
21}
22
23func (ks *Server) encryptWithPgp(key *PgpKey, plaintext []byte) ([]byte, error) {
24	pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
25	err := pgpKey.Encrypt(plaintext)
26	if err != nil {
27		return nil, err
28	}
29	return []byte(pgpKey.EncryptedKey), nil
30}
31
32func (ks *Server) encryptWithKms(key *KmsKey, plaintext []byte) ([]byte, error) {
33	kmsKey := kmsKeyToMasterKey(key)
34	err := kmsKey.Encrypt(plaintext)
35	if err != nil {
36		return nil, err
37	}
38	return []byte(kmsKey.EncryptedKey), nil
39}
40
41func (ks *Server) encryptWithGcpKms(key *GcpKmsKey, plaintext []byte) ([]byte, error) {
42	gcpKmsKey := gcpkms.MasterKey{
43		ResourceID: key.ResourceId,
44	}
45	err := gcpKmsKey.Encrypt(plaintext)
46	if err != nil {
47		return nil, err
48	}
49	return []byte(gcpKmsKey.EncryptedKey), nil
50}
51
52func (ks *Server) encryptWithAzureKeyVault(key *AzureKeyVaultKey, plaintext []byte) ([]byte, error) {
53	azkvKey := azkv.MasterKey{
54		VaultURL: key.VaultUrl,
55		Name:     key.Name,
56		Version:  key.Version,
57	}
58	err := azkvKey.Encrypt(plaintext)
59	if err != nil {
60		return nil, err
61	}
62	return []byte(azkvKey.EncryptedKey), nil
63}
64
65func (ks *Server) encryptWithVault(key *VaultKey, plaintext []byte) ([]byte, error) {
66	vaultKey := hcvault.MasterKey{
67		VaultAddress: key.VaultAddress,
68		EnginePath:   key.EnginePath,
69		KeyName:      key.KeyName,
70	}
71	err := vaultKey.Encrypt(plaintext)
72	if err != nil {
73		return nil, err
74	}
75	return []byte(vaultKey.EncryptedKey), nil
76}
77
78func (ks *Server) decryptWithPgp(key *PgpKey, ciphertext []byte) ([]byte, error) {
79	pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
80	pgpKey.EncryptedKey = string(ciphertext)
81	plaintext, err := pgpKey.Decrypt()
82	return []byte(plaintext), err
83}
84
85func (ks *Server) decryptWithKms(key *KmsKey, ciphertext []byte) ([]byte, error) {
86	kmsKey := kmsKeyToMasterKey(key)
87	kmsKey.EncryptedKey = string(ciphertext)
88	plaintext, err := kmsKey.Decrypt()
89	return []byte(plaintext), err
90}
91
92func (ks *Server) decryptWithGcpKms(key *GcpKmsKey, ciphertext []byte) ([]byte, error) {
93	gcpKmsKey := gcpkms.MasterKey{
94		ResourceID: key.ResourceId,
95	}
96	gcpKmsKey.EncryptedKey = string(ciphertext)
97	plaintext, err := gcpKmsKey.Decrypt()
98	return []byte(plaintext), err
99}
100
101func (ks *Server) decryptWithAzureKeyVault(key *AzureKeyVaultKey, ciphertext []byte) ([]byte, error) {
102	azkvKey := azkv.MasterKey{
103		VaultURL: key.VaultUrl,
104		Name:     key.Name,
105		Version:  key.Version,
106	}
107	azkvKey.EncryptedKey = string(ciphertext)
108	plaintext, err := azkvKey.Decrypt()
109	return []byte(plaintext), err
110}
111
112func (ks *Server) decryptWithVault(key *VaultKey, ciphertext []byte) ([]byte, error) {
113	vaultKey := hcvault.MasterKey{
114		VaultAddress: key.VaultAddress,
115		EnginePath:   key.EnginePath,
116		KeyName:      key.KeyName,
117	}
118	vaultKey.EncryptedKey = string(ciphertext)
119	plaintext, err := vaultKey.Decrypt()
120	return []byte(plaintext), err
121}
122
123// Encrypt takes an encrypt request and encrypts the provided plaintext with the provided key, returning the encrypted
124// result
125func (ks Server) Encrypt(ctx context.Context,
126	req *EncryptRequest) (*EncryptResponse, error) {
127	key := *req.Key
128	var response *EncryptResponse
129	switch k := key.KeyType.(type) {
130	case *Key_PgpKey:
131		ciphertext, err := ks.encryptWithPgp(k.PgpKey, req.Plaintext)
132		if err != nil {
133			return nil, err
134		}
135		response = &EncryptResponse{
136			Ciphertext: ciphertext,
137		}
138	case *Key_KmsKey:
139		ciphertext, err := ks.encryptWithKms(k.KmsKey, req.Plaintext)
140		if err != nil {
141			return nil, err
142		}
143		response = &EncryptResponse{
144			Ciphertext: ciphertext,
145		}
146	case *Key_GcpKmsKey:
147		ciphertext, err := ks.encryptWithGcpKms(k.GcpKmsKey, req.Plaintext)
148		if err != nil {
149			return nil, err
150		}
151		response = &EncryptResponse{
152			Ciphertext: ciphertext,
153		}
154	case *Key_AzureKeyvaultKey:
155		ciphertext, err := ks.encryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Plaintext)
156		if err != nil {
157			return nil, err
158		}
159		response = &EncryptResponse{
160			Ciphertext: ciphertext,
161		}
162	case *Key_VaultKey:
163		ciphertext, err := ks.encryptWithVault(k.VaultKey, req.Plaintext)
164		if err != nil {
165			return nil, err
166		}
167		response = &EncryptResponse{
168			Ciphertext: ciphertext,
169		}
170	case nil:
171		return nil, status.Errorf(codes.NotFound, "Must provide a key")
172	default:
173		return nil, status.Errorf(codes.NotFound, "Unknown key type")
174	}
175	if ks.Prompt {
176		err := ks.prompt(key, "encrypt")
177		if err != nil {
178			return nil, err
179		}
180	}
181	return response, nil
182}
183
184func keyToString(key Key) string {
185	switch k := key.KeyType.(type) {
186	case *Key_PgpKey:
187		return fmt.Sprintf("PGP key with fingerprint %s", k.PgpKey.Fingerprint)
188	case *Key_KmsKey:
189		return fmt.Sprintf("AWS KMS key with ARN %s", k.KmsKey.Arn)
190	case *Key_GcpKmsKey:
191		return fmt.Sprintf("GCP KMS key with resource ID %s", k.GcpKmsKey.ResourceId)
192	case *Key_AzureKeyvaultKey:
193		return fmt.Sprintf("Azure Key Vault key with URL %s/keys/%s/%s", k.AzureKeyvaultKey.VaultUrl, k.AzureKeyvaultKey.Name, k.AzureKeyvaultKey.Version)
194	case *Key_VaultKey:
195		return fmt.Sprintf("Hashicorp Vault key with URI %s/v1/%s/keys/%s", k.VaultKey.VaultAddress, k.VaultKey.EnginePath, k.VaultKey.KeyName)
196	default:
197		return fmt.Sprintf("Unknown key type")
198	}
199}
200
201func (ks Server) prompt(key Key, requestType string) error {
202	keyString := keyToString(key)
203	var response string
204	for response != "y" && response != "n" {
205		fmt.Printf("\nReceived %s request using %s. Respond to request? (y/n): ", requestType, keyString)
206		_, err := fmt.Scanln(&response)
207		if err != nil {
208			return err
209		}
210	}
211	if response == "n" {
212		return grpc.Errorf(codes.PermissionDenied, "Request rejected by user")
213	}
214	return nil
215}
216
217// Decrypt takes a decrypt request and decrypts the provided ciphertext with the provided key, returning the decrypted
218// result
219func (ks Server) Decrypt(ctx context.Context,
220	req *DecryptRequest) (*DecryptResponse, error) {
221	key := *req.Key
222	var response *DecryptResponse
223	switch k := key.KeyType.(type) {
224	case *Key_PgpKey:
225		plaintext, err := ks.decryptWithPgp(k.PgpKey, req.Ciphertext)
226		if err != nil {
227			return nil, err
228		}
229		response = &DecryptResponse{
230			Plaintext: plaintext,
231		}
232	case *Key_KmsKey:
233		plaintext, err := ks.decryptWithKms(k.KmsKey, req.Ciphertext)
234		if err != nil {
235			return nil, err
236		}
237		response = &DecryptResponse{
238			Plaintext: plaintext,
239		}
240	case *Key_GcpKmsKey:
241		plaintext, err := ks.decryptWithGcpKms(k.GcpKmsKey, req.Ciphertext)
242		if err != nil {
243			return nil, err
244		}
245		response = &DecryptResponse{
246			Plaintext: plaintext,
247		}
248	case *Key_AzureKeyvaultKey:
249		plaintext, err := ks.decryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Ciphertext)
250		if err != nil {
251			return nil, err
252		}
253		response = &DecryptResponse{
254			Plaintext: plaintext,
255		}
256	case *Key_VaultKey:
257		plaintext, err := ks.decryptWithVault(k.VaultKey, req.Ciphertext)
258		if err != nil {
259			return nil, err
260		}
261		response = &DecryptResponse{
262			Plaintext: plaintext,
263		}
264	case nil:
265		return nil, grpc.Errorf(codes.NotFound, "Must provide a key")
266	default:
267		return nil, grpc.Errorf(codes.NotFound, "Unknown key type")
268	}
269	if ks.Prompt {
270		err := ks.prompt(key, "decrypt")
271		if err != nil {
272			return nil, err
273		}
274	}
275	return response, nil
276}
277
278func kmsKeyToMasterKey(key *KmsKey) kms.MasterKey {
279	ctx := make(map[string]*string)
280	for k, v := range key.Context {
281		value := v // Allocate a new string to prevent the pointer below from referring to only the last iteration value
282		ctx[k] = &value
283	}
284	return kms.MasterKey{
285		Arn:               key.Arn,
286		Role:              key.Role,
287		EncryptionContext: ctx,
288		AwsProfile:        key.AwsProfile,
289	}
290}
291