-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
kms_key_handler.go
237 lines (203 loc) · 7.73 KB
/
kms_key_handler.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
package s3crypto
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/kms/kmsiface"
)
const (
// KMSWrap is a constant used during decryption to build a KMS key handler.
KMSWrap = "kms"
)
// kmsKeyHandler will make calls to KMS to get the masterkey
type kmsKeyHandler struct {
kms kmsiface.KMSAPI
cmkID *string
// useProvidedCMK is toggled when using `kms` key wrapper with V2 client
useProvidedCMK bool
CipherData
}
// NewKMSKeyGenerator builds a new KMS key provider using the customer key ID and material
// description.
//
// Example:
//
// sess := session.Must(session.NewSession())
// cmkID := "arn to key"
// matdesc := s3crypto.MaterialDescription{}
// handler := s3crypto.NewKMSKeyGenerator(kms.New(sess), cmkID)
//
// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
func NewKMSKeyGenerator(kmsClient kmsiface.KMSAPI, cmkID string) CipherDataGenerator {
return NewKMSKeyGeneratorWithMatDesc(kmsClient, cmkID, MaterialDescription{})
}
func newKMSKeyHandler(client kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) *kmsKeyHandler {
// These values are read only making them thread safe
kp := &kmsKeyHandler{
kms: client,
cmkID: &cmkID,
}
if matdesc == nil {
matdesc = MaterialDescription{}
}
matdesc["kms_cmk_id"] = &cmkID
kp.CipherData.WrapAlgorithm = KMSWrap
kp.CipherData.MaterialDescription = matdesc
return kp
}
// NewKMSKeyGeneratorWithMatDesc builds a new KMS key provider using the customer key ID and material
// description.
//
// Example:
//
// sess := session.Must(session.NewSession())
// cmkID := "arn to key"
// matdesc := s3crypto.MaterialDescription{}
// handler := s3crypto.NewKMSKeyGeneratorWithMatDesc(kms.New(sess), cmkID, matdesc)
//
// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
func NewKMSKeyGeneratorWithMatDesc(kmsClient kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) CipherDataGenerator {
return newKMSKeyHandler(kmsClient, cmkID, matdesc)
}
// NewKMSWrapEntry builds returns a new KMS key provider and its decrypt handler.
//
// Example:
//
// sess := session.Must(session.NewSession())
// customKMSClient := kms.New(sess)
// decryptHandler := s3crypto.NewKMSWrapEntry(customKMSClient)
//
// svc := s3crypto.NewDecryptionClient(sess, func(svc *s3crypto.DecryptionClient) {
// svc.WrapRegistry[s3crypto.KMSWrap] = decryptHandler
// }))
//
// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
func NewKMSWrapEntry(kmsClient kmsiface.KMSAPI) WrapEntry {
kp := newKMSWrapEntry(kmsClient)
return kp.decryptHandler
}
// RegisterKMSWrapWithCMK registers the `kms` wrapping algorithm to the given WrapRegistry. The wrapper will be
// configured to call KMS Decrypt with the provided CMK.
//
// Example:
//
// sess := session.Must(session.NewSession())
// cr := s3crypto.NewCryptoRegistry()
// if err := s3crypto.RegisterKMSWrapWithCMK(cr, kms.New(sess), "cmkId"); err != nil {
// panic(err) // handle error
// }
//
// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
func RegisterKMSWrapWithCMK(registry *CryptoRegistry, client kmsiface.KMSAPI, cmkID string) error {
if registry == nil {
return errNilCryptoRegistry
}
return registry.AddWrap(KMSWrap, newKMSWrapEntryWithCMK(client, cmkID))
}
// RegisterKMSWrapWithAnyCMK registers the `kms` wrapping algorithm to the given WrapRegistry. The wrapper will be
// configured to call KMS Decrypt without providing a CMK.
//
// Example:
//
// sess := session.Must(session.NewSession())
// cr := s3crypto.NewCryptoRegistry()
// if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kms.New(sess)); err != nil {
// panic(err) // handle error
// }
//
// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
func RegisterKMSWrapWithAnyCMK(registry *CryptoRegistry, client kmsiface.KMSAPI) error {
if registry == nil {
return errNilCryptoRegistry
}
return registry.AddWrap(KMSWrap, NewKMSWrapEntry(client))
}
// newKMSWrapEntryWithCMK builds returns a new KMS key provider and its decrypt handler. The wrap entry will be configured
// to only attempt to decrypt the data key using the provided CMK.
func newKMSWrapEntryWithCMK(kmsClient kmsiface.KMSAPI, cmkID string) WrapEntry {
kp := newKMSWrapEntry(kmsClient)
kp.useProvidedCMK = true
kp.cmkID = &cmkID
return kp.decryptHandler
}
func newKMSWrapEntry(kmsClient kmsiface.KMSAPI) *kmsKeyHandler {
// These values are read only making them thread safe
kp := &kmsKeyHandler{
kms: kmsClient,
}
return kp
}
// decryptHandler initializes a KMS keyprovider with a material description. This
// is used with Decrypting kms content, due to the cmkID being in the material description.
func (kp kmsKeyHandler) decryptHandler(env Envelope) (CipherDataDecrypter, error) {
m := MaterialDescription{}
err := m.decodeDescription([]byte(env.MatDesc))
if err != nil {
return nil, err
}
_, ok := m["kms_cmk_id"]
if !ok {
return nil, awserr.New("MissingCMKIDError", "Material description is missing CMK ID", nil)
}
kp.CipherData.MaterialDescription = m
kp.WrapAlgorithm = KMSWrap
return &kp, nil
}
// DecryptKey makes a call to KMS to decrypt the key.
func (kp *kmsKeyHandler) DecryptKey(key []byte) ([]byte, error) {
return kp.DecryptKeyWithContext(aws.BackgroundContext(), key)
}
// DecryptKeyWithContext makes a call to KMS to decrypt the key with request context.
func (kp *kmsKeyHandler) DecryptKeyWithContext(ctx aws.Context, key []byte) ([]byte, error) {
in := &kms.DecryptInput{
EncryptionContext: kp.MaterialDescription,
CiphertextBlob: key,
GrantTokens: []*string{},
}
// useProvidedCMK will be true if a constructor was used with the new V2 client
if kp.useProvidedCMK {
in.KeyId = kp.cmkID
}
out, err := kp.kms.DecryptWithContext(ctx, in)
if err != nil {
return nil, err
}
return out.Plaintext, nil
}
// GenerateCipherData makes a call to KMS to generate a data key, Upon making
// the call, it also sets the encrypted key.
func (kp *kmsKeyHandler) GenerateCipherData(keySize, ivSize int) (CipherData, error) {
return kp.GenerateCipherDataWithContext(aws.BackgroundContext(), keySize, ivSize)
}
// GenerateCipherDataWithContext makes a call to KMS to generate a data key,
// Upon making the call, it also sets the encrypted key.
func (kp *kmsKeyHandler) GenerateCipherDataWithContext(ctx aws.Context, keySize, ivSize int) (CipherData, error) {
cd := kp.CipherData.Clone()
out, err := kp.kms.GenerateDataKeyWithContext(ctx,
&kms.GenerateDataKeyInput{
EncryptionContext: cd.MaterialDescription,
KeyId: kp.cmkID,
KeySpec: aws.String("AES_256"),
})
if err != nil {
return CipherData{}, err
}
iv, err := generateBytes(ivSize)
if err != nil {
return CipherData{}, err
}
cd.Key = out.Plaintext
cd.IV = iv
cd.EncryptedKey = out.CiphertextBlob
return cd, nil
}
func (kp kmsKeyHandler) isAWSFixture() bool {
return true
}
var (
_ CipherDataGenerator = (*kmsKeyHandler)(nil)
_ CipherDataGeneratorWithContext = (*kmsKeyHandler)(nil)
_ CipherDataDecrypter = (*kmsKeyHandler)(nil)
_ CipherDataDecrypterWithContext = (*kmsKeyHandler)(nil)
_ awsFixture = (*kmsKeyHandler)(nil)
)