-
Notifications
You must be signed in to change notification settings - Fork 9
/
path_reencrypt.go
142 lines (120 loc) · 4.25 KB
/
path_reencrypt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package gcpkms
import (
"context"
"encoding/base64"
"fmt"
"path"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)
func (b *backend) pathReencrypt() *framework.Path {
return &framework.Path{
Pattern: "reencrypt/" + framework.GenericNameRegex("key"),
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixGoogleCloudKMS,
OperationVerb: "reencrypt",
},
HelpSynopsis: "Re-encrypt existing ciphertext data to a new version",
HelpDescription: `
Use the named encryption key to re-encrypt the underlying cryptokey to the latest
version for this ciphertext without disclosing the original plaintext value to
the requestor.
`,
Fields: map[string]*framework.FieldSchema{
"key": &framework.FieldSchema{
Type: framework.TypeString,
Description: `
Name of the key to use for encryption. This key must already exist in Vault and
Google Cloud KMS.
`,
},
"additional_authenticated_data": &framework.FieldSchema{
Type: framework.TypeString,
Description: `
Optional data that, if specified, must also be provided during decryption.
`,
},
"ciphertext": &framework.FieldSchema{
Type: framework.TypeString,
Description: `
Ciphertext to be re-encrypted to the latest key version. This must be ciphertext
that Vault previously generated for this named key.
`,
},
"key_version": &framework.FieldSchema{
Type: framework.TypeInt,
Description: `
Integer version of the crypto key version to use for the new encryption. If
unspecified, this defaults to the latest active crypto key version.
`,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: withFieldValidator(b.pathReencryptWrite),
},
}
}
// pathReencryptWrite corresponds to PUT/POST gcpkms/reencrypt/:key and is
// used to re-encrypt the given ciphertext to the latest cryptokey version.
func (b *backend) pathReencryptWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
key := d.Get("key").(string)
aad := d.Get("additional_authenticated_data").(string)
keyVersion := d.Get("key_version").(int)
k, err := b.Key(ctx, req.Storage, key)
if err != nil {
if err == ErrKeyNotFound {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
return nil, err
}
cryptoKey := k.CryptoKeyID
if keyVersion > 0 {
if k.MinVersion > 0 && keyVersion < k.MinVersion {
resp := fmt.Sprintf("requested version %d is less than minimum allowed version of %d",
keyVersion, k.MinVersion)
return logical.ErrorResponse(resp), logical.ErrPermissionDenied
}
if k.MaxVersion > 0 && keyVersion > k.MaxVersion {
resp := fmt.Sprintf("requested version %d is greater than maximum allowed version of %d",
keyVersion, k.MaxVersion)
return logical.ErrorResponse(resp), logical.ErrPermissionDenied
}
cryptoKey = fmt.Sprintf("%s/cryptoKeyVersions/%d", cryptoKey, keyVersion)
}
// We gave the user back base64-encoded ciphertext in the /encrypt payload
ciphertext, err := base64.StdEncoding.DecodeString(d.Get("ciphertext").(string))
if err != nil {
return nil, errwrap.Wrapf("failed to base64 decode ciphtertext: {{err}}", err)
}
kmsClient, closer, err := b.KMSClient(req.Storage)
if err != nil {
return nil, err
}
defer closer()
decResp, err := kmsClient.Decrypt(ctx, &kmspb.DecryptRequest{
Name: k.CryptoKeyID, // KMS chooses the version
Ciphertext: ciphertext,
AdditionalAuthenticatedData: []byte(aad),
})
if err != nil {
return nil, errwrap.Wrapf("failed to decrypt ciphertext: {{err}}", err)
}
encResp, err := kmsClient.Encrypt(ctx, &kmspb.EncryptRequest{
Name: cryptoKey, // User-specified version
Plaintext: decResp.Plaintext,
AdditionalAuthenticatedData: []byte(aad),
})
if err != nil {
return nil, errwrap.Wrapf("failed to encrypt new plaintext: {{err}}", err)
}
return &logical.Response{
Data: map[string]interface{}{
"key_version": path.Base(encResp.Name),
"ciphertext": base64.StdEncoding.EncodeToString(encResp.Ciphertext),
},
}, nil
}