1package s3crypto 2 3import ( 4 "github.com/aws/aws-sdk-go/aws" 5 "github.com/aws/aws-sdk-go/aws/awserr" 6 "github.com/aws/aws-sdk-go/service/kms" 7 "github.com/aws/aws-sdk-go/service/kms/kmsiface" 8) 9 10const ( 11 // KMSWrap is a constant used during decryption to build a KMS key handler. 12 KMSWrap = "kms" 13) 14 15// kmsKeyHandler will make calls to KMS to get the masterkey 16type kmsKeyHandler struct { 17 kms kmsiface.KMSAPI 18 cmkID *string 19 20 // useProvidedCMK is toggled when using `kms` key wrapper with V2 client 21 useProvidedCMK bool 22 23 CipherData 24} 25 26// NewKMSKeyGenerator builds a new KMS key provider using the customer key ID and material 27// description. 28// 29// Example: 30// sess := session.Must(session.NewSession()) 31// cmkID := "arn to key" 32// matdesc := s3crypto.MaterialDescription{} 33// handler := s3crypto.NewKMSKeyGenerator(kms.New(sess), cmkID) 34// 35// 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. 36func NewKMSKeyGenerator(kmsClient kmsiface.KMSAPI, cmkID string) CipherDataGenerator { 37 return NewKMSKeyGeneratorWithMatDesc(kmsClient, cmkID, MaterialDescription{}) 38} 39 40func newKMSKeyHandler(client kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) *kmsKeyHandler { 41 // These values are read only making them thread safe 42 kp := &kmsKeyHandler{ 43 kms: client, 44 cmkID: &cmkID, 45 } 46 47 if matdesc == nil { 48 matdesc = MaterialDescription{} 49 } 50 51 matdesc["kms_cmk_id"] = &cmkID 52 53 kp.CipherData.WrapAlgorithm = KMSWrap 54 kp.CipherData.MaterialDescription = matdesc 55 56 return kp 57} 58 59// NewKMSKeyGeneratorWithMatDesc builds a new KMS key provider using the customer key ID and material 60// description. 61// 62// Example: 63// sess := session.Must(session.NewSession()) 64// cmkID := "arn to key" 65// matdesc := s3crypto.MaterialDescription{} 66// handler := s3crypto.NewKMSKeyGeneratorWithMatDesc(kms.New(sess), cmkID, matdesc) 67// 68// 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. 69func NewKMSKeyGeneratorWithMatDesc(kmsClient kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) CipherDataGenerator { 70 return newKMSKeyHandler(kmsClient, cmkID, matdesc) 71} 72 73// NewKMSWrapEntry builds returns a new KMS key provider and its decrypt handler. 74// 75// Example: 76// sess := session.Must(session.NewSession()) 77// customKMSClient := kms.New(sess) 78// decryptHandler := s3crypto.NewKMSWrapEntry(customKMSClient) 79// 80// svc := s3crypto.NewDecryptionClient(sess, func(svc *s3crypto.DecryptionClient) { 81// svc.WrapRegistry[s3crypto.KMSWrap] = decryptHandler 82// })) 83// 84// 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. 85func NewKMSWrapEntry(kmsClient kmsiface.KMSAPI) WrapEntry { 86 kp := newKMSWrapEntry(kmsClient) 87 return kp.decryptHandler 88} 89 90// RegisterKMSWrapWithCMK registers the `kms` wrapping algorithm to the given WrapRegistry. The wrapper will be 91// configured to call KMS Decrypt with the provided CMK. 92// 93// Example: 94// sess := session.Must(session.NewSession()) 95// cr := s3crypto.NewCryptoRegistry() 96// if err := s3crypto.RegisterKMSWrapWithCMK(cr, kms.New(sess), "cmkId"); err != nil { 97// panic(err) // handle error 98// } 99// 100// 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. 101func RegisterKMSWrapWithCMK(registry *CryptoRegistry, client kmsiface.KMSAPI, cmkID string) error { 102 if registry == nil { 103 return errNilCryptoRegistry 104 } 105 return registry.AddWrap(KMSWrap, newKMSWrapEntryWithCMK(client, cmkID)) 106} 107 108// RegisterKMSWrapWithAnyCMK registers the `kms` wrapping algorithm to the given WrapRegistry. The wrapper will be 109// configured to call KMS Decrypt without providing a CMK. 110// 111// Example: 112// sess := session.Must(session.NewSession()) 113// cr := s3crypto.NewCryptoRegistry() 114// if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kms.New(sess)); err != nil { 115// panic(err) // handle error 116// } 117// 118// 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. 119func RegisterKMSWrapWithAnyCMK(registry *CryptoRegistry, client kmsiface.KMSAPI) error { 120 if registry == nil { 121 return errNilCryptoRegistry 122 } 123 return registry.AddWrap(KMSWrap, NewKMSWrapEntry(client)) 124} 125 126// newKMSWrapEntryWithCMK builds returns a new KMS key provider and its decrypt handler. The wrap entry will be configured 127// to only attempt to decrypt the data key using the provided CMK. 128func newKMSWrapEntryWithCMK(kmsClient kmsiface.KMSAPI, cmkID string) WrapEntry { 129 kp := newKMSWrapEntry(kmsClient) 130 kp.useProvidedCMK = true 131 kp.cmkID = &cmkID 132 return kp.decryptHandler 133} 134 135func newKMSWrapEntry(kmsClient kmsiface.KMSAPI) *kmsKeyHandler { 136 // These values are read only making them thread safe 137 kp := &kmsKeyHandler{ 138 kms: kmsClient, 139 } 140 141 return kp 142} 143 144// decryptHandler initializes a KMS keyprovider with a material description. This 145// is used with Decrypting kms content, due to the cmkID being in the material description. 146func (kp kmsKeyHandler) decryptHandler(env Envelope) (CipherDataDecrypter, error) { 147 m := MaterialDescription{} 148 err := m.decodeDescription([]byte(env.MatDesc)) 149 if err != nil { 150 return nil, err 151 } 152 153 _, ok := m["kms_cmk_id"] 154 if !ok { 155 return nil, awserr.New("MissingCMKIDError", "Material description is missing CMK ID", nil) 156 } 157 158 kp.CipherData.MaterialDescription = m 159 kp.WrapAlgorithm = KMSWrap 160 161 return &kp, nil 162} 163 164// DecryptKey makes a call to KMS to decrypt the key. 165func (kp *kmsKeyHandler) DecryptKey(key []byte) ([]byte, error) { 166 return kp.DecryptKeyWithContext(aws.BackgroundContext(), key) 167} 168 169// DecryptKeyWithContext makes a call to KMS to decrypt the key with request context. 170func (kp *kmsKeyHandler) DecryptKeyWithContext(ctx aws.Context, key []byte) ([]byte, error) { 171 in := &kms.DecryptInput{ 172 EncryptionContext: kp.MaterialDescription, 173 CiphertextBlob: key, 174 GrantTokens: []*string{}, 175 } 176 177 // useProvidedCMK will be true if a constructor was used with the new V2 client 178 if kp.useProvidedCMK { 179 in.KeyId = kp.cmkID 180 } 181 182 out, err := kp.kms.DecryptWithContext(ctx, in) 183 if err != nil { 184 return nil, err 185 } 186 return out.Plaintext, nil 187} 188 189// GenerateCipherData makes a call to KMS to generate a data key, Upon making 190// the call, it also sets the encrypted key. 191func (kp *kmsKeyHandler) GenerateCipherData(keySize, ivSize int) (CipherData, error) { 192 return kp.GenerateCipherDataWithContext(aws.BackgroundContext(), keySize, ivSize) 193} 194 195// GenerateCipherDataWithContext makes a call to KMS to generate a data key, 196// Upon making the call, it also sets the encrypted key. 197func (kp *kmsKeyHandler) GenerateCipherDataWithContext(ctx aws.Context, keySize, ivSize int) (CipherData, error) { 198 cd := kp.CipherData.Clone() 199 200 out, err := kp.kms.GenerateDataKeyWithContext(ctx, 201 &kms.GenerateDataKeyInput{ 202 EncryptionContext: cd.MaterialDescription, 203 KeyId: kp.cmkID, 204 KeySpec: aws.String("AES_256"), 205 }) 206 if err != nil { 207 return CipherData{}, err 208 } 209 210 iv, err := generateBytes(ivSize) 211 if err != nil { 212 return CipherData{}, err 213 } 214 215 cd.Key = out.Plaintext 216 cd.IV = iv 217 cd.EncryptedKey = out.CiphertextBlob 218 219 return cd, nil 220} 221 222func (kp kmsKeyHandler) isAWSFixture() bool { 223 return true 224} 225 226var ( 227 _ CipherDataGenerator = (*kmsKeyHandler)(nil) 228 _ CipherDataGeneratorWithContext = (*kmsKeyHandler)(nil) 229 _ CipherDataDecrypter = (*kmsKeyHandler)(nil) 230 _ CipherDataDecrypterWithContext = (*kmsKeyHandler)(nil) 231 _ awsFixture = (*kmsKeyHandler)(nil) 232) 233