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