github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/s3crypto/kms_context_key_handler.go (about)

     1  package s3crypto
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/aavshr/aws-sdk-go/aws"
     7  	"github.com/aavshr/aws-sdk-go/service/kms"
     8  	"github.com/aavshr/aws-sdk-go/service/kms/kmsiface"
     9  )
    10  
    11  const (
    12  	// KMSContextWrap is a constant used during decryption to build a kms+context key handler
    13  	KMSContextWrap      = "kms+context"
    14  	kmsAWSCEKContextKey = "aws:" + cekAlgorithmHeader
    15  
    16  	kmsReservedKeyConflictErrMsg = "conflict in reserved KMS Encryption Context key %s. This value is reserved for the S3 Encryption Client and cannot be set by the user"
    17  	kmsMismatchCEKAlg            = "the content encryption algorithm used at encryption time does not match the algorithm stored for decryption time. The object may be altered or corrupted"
    18  )
    19  
    20  // NewKMSContextKeyGenerator builds a new kms+context key provider using the customer key ID and material
    21  // description.
    22  //
    23  // Example:
    24  //	sess := session.Must(session.NewSession())
    25  //	cmkID := "KMS Key ARN"
    26  //	var matdesc s3crypto.MaterialDescription
    27  //	handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID, matdesc)
    28  func NewKMSContextKeyGenerator(client kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) CipherDataGeneratorWithCEKAlg {
    29  	return newKMSContextKeyHandler(client, cmkID, matdesc)
    30  }
    31  
    32  // RegisterKMSContextWrapWithCMK registers the kms+context wrapping algorithm to the given WrapRegistry. The wrapper
    33  // will be configured to only call KMS Decrypt using the provided CMK.
    34  //
    35  // Example:
    36  //	cr := s3crypto.NewCryptoRegistry()
    37  //	if err := RegisterKMSContextWrapWithCMK(); err != nil {
    38  //		panic(err) // handle error
    39  //	}
    40  func RegisterKMSContextWrapWithCMK(registry *CryptoRegistry, client kmsiface.KMSAPI, cmkID string) error {
    41  	if registry == nil {
    42  		return errNilCryptoRegistry
    43  	}
    44  	return registry.AddWrap(KMSContextWrap, newKMSContextWrapEntryWithCMK(client, cmkID))
    45  }
    46  
    47  // RegisterKMSContextWrapWithAnyCMK registers the kms+context wrapping algorithm to the given WrapRegistry. The wrapper
    48  // will be configured to call KMS decrypt without providing a CMK.
    49  //
    50  // Example:
    51  //	sess := session.Must(session.NewSession())
    52  //	cr := s3crypto.NewCryptoRegistry()
    53  //	if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kms.New(sess)); err != nil {
    54  //		panic(err) // handle error
    55  //	}
    56  func RegisterKMSContextWrapWithAnyCMK(registry *CryptoRegistry, client kmsiface.KMSAPI) error {
    57  	if registry == nil {
    58  		return errNilCryptoRegistry
    59  	}
    60  	return registry.AddWrap(KMSContextWrap, newKMSContextWrapEntryWithAnyCMK(client))
    61  }
    62  
    63  // newKMSContextWrapEntryWithCMK builds returns a new kms+context key provider and its decrypt handler.
    64  // The returned handler will be configured to calls KMS Decrypt API without specifying a specific KMS CMK.
    65  func newKMSContextWrapEntryWithCMK(kmsClient kmsiface.KMSAPI, cmkID string) WrapEntry {
    66  	// These values are read only making them thread safe
    67  	kp := &kmsContextKeyHandler{
    68  		kms:   kmsClient,
    69  		cmkID: &cmkID,
    70  	}
    71  
    72  	return kp.decryptHandler
    73  }
    74  
    75  // newKMSContextWrapEntryWithAnyCMK builds returns a new kms+context key provider and its decrypt handler.
    76  // The returned handler will be configured to calls KMS Decrypt API without specifying a specific KMS CMK.
    77  func newKMSContextWrapEntryWithAnyCMK(kmsClient kmsiface.KMSAPI) WrapEntry {
    78  	// These values are read only making them thread safe
    79  	kp := &kmsContextKeyHandler{
    80  		kms: kmsClient,
    81  	}
    82  
    83  	return kp.decryptHandler
    84  }
    85  
    86  // kmsContextKeyHandler wraps the kmsKeyHandler to explicitly make this type incompatible with the v1 client
    87  // by not exposing the old interface implementations.
    88  type kmsContextKeyHandler struct {
    89  	kms   kmsiface.KMSAPI
    90  	cmkID *string
    91  
    92  	CipherData
    93  }
    94  
    95  func (kp *kmsContextKeyHandler) isAWSFixture() bool {
    96  	return true
    97  }
    98  
    99  func newKMSContextKeyHandler(client kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) *kmsContextKeyHandler {
   100  	kp := &kmsContextKeyHandler{
   101  		kms:   client,
   102  		cmkID: &cmkID,
   103  	}
   104  
   105  	if matdesc == nil {
   106  		matdesc = MaterialDescription{}
   107  	}
   108  
   109  	kp.CipherData.WrapAlgorithm = KMSContextWrap
   110  	kp.CipherData.MaterialDescription = matdesc
   111  
   112  	return kp
   113  }
   114  
   115  func (kp *kmsContextKeyHandler) GenerateCipherDataWithCEKAlg(ctx aws.Context, keySize int, ivSize int, cekAlgorithm string) (CipherData, error) {
   116  	cd := kp.CipherData.Clone()
   117  
   118  	if len(cekAlgorithm) == 0 {
   119  		return CipherData{}, fmt.Errorf("cek algorithm identifier must not be empty")
   120  	}
   121  
   122  	if _, ok := cd.MaterialDescription[kmsAWSCEKContextKey]; ok {
   123  		return CipherData{}, fmt.Errorf(kmsReservedKeyConflictErrMsg, kmsAWSCEKContextKey)
   124  	}
   125  	cd.MaterialDescription[kmsAWSCEKContextKey] = &cekAlgorithm
   126  
   127  	out, err := kp.kms.GenerateDataKeyWithContext(ctx,
   128  		&kms.GenerateDataKeyInput{
   129  			EncryptionContext: cd.MaterialDescription,
   130  			KeyId:             kp.cmkID,
   131  			KeySpec:           aws.String("AES_256"),
   132  		})
   133  	if err != nil {
   134  		return CipherData{}, err
   135  	}
   136  
   137  	iv, err := generateBytes(ivSize)
   138  	if err != nil {
   139  		return CipherData{}, err
   140  	}
   141  
   142  	cd.Key = out.Plaintext
   143  	cd.IV = iv
   144  	cd.EncryptedKey = out.CiphertextBlob
   145  
   146  	return cd, nil
   147  }
   148  
   149  // decryptHandler initializes a KMS keyprovider with a material description. This
   150  // is used with Decrypting kms content, due to the cmkID being in the material description.
   151  func (kp kmsContextKeyHandler) decryptHandler(env Envelope) (CipherDataDecrypter, error) {
   152  	if env.WrapAlg != KMSContextWrap {
   153  		return nil, fmt.Errorf("%s value `%s` did not match the expected algorithm `%s` for this handler", cekAlgorithmHeader, env.WrapAlg, KMSContextWrap)
   154  	}
   155  
   156  	m := MaterialDescription{}
   157  	err := m.decodeDescription([]byte(env.MatDesc))
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	if v, ok := m[kmsAWSCEKContextKey]; !ok {
   163  		return nil, fmt.Errorf("required key %v is missing from encryption context", kmsAWSCEKContextKey)
   164  	} else if v == nil || *v != env.CEKAlg {
   165  		return nil, fmt.Errorf(kmsMismatchCEKAlg)
   166  	}
   167  
   168  	kp.MaterialDescription = m
   169  	kp.WrapAlgorithm = KMSContextWrap
   170  
   171  	return &kp, nil
   172  }
   173  
   174  // DecryptKey makes a call to KMS to decrypt the key.
   175  func (kp *kmsContextKeyHandler) DecryptKey(key []byte) ([]byte, error) {
   176  	return kp.DecryptKeyWithContext(aws.BackgroundContext(), key)
   177  }
   178  
   179  // DecryptKeyWithContext makes a call to KMS to decrypt the key with request context.
   180  func (kp *kmsContextKeyHandler) DecryptKeyWithContext(ctx aws.Context, key []byte) ([]byte, error) {
   181  	out, err := kp.kms.DecryptWithContext(ctx,
   182  		&kms.DecryptInput{
   183  			KeyId:             kp.cmkID, // will be nil and not serialized if created with the AnyCMK constructor
   184  			EncryptionContext: kp.MaterialDescription,
   185  			CiphertextBlob:    key,
   186  			GrantTokens:       []*string{},
   187  		})
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	return out.Plaintext, nil
   192  }
   193  
   194  var (
   195  	_ CipherDataGeneratorWithCEKAlg  = (*kmsContextKeyHandler)(nil)
   196  	_ CipherDataDecrypter            = (*kmsContextKeyHandler)(nil)
   197  	_ CipherDataDecrypterWithContext = (*kmsContextKeyHandler)(nil)
   198  	_ awsFixture                     = (*kmsContextKeyHandler)(nil)
   199  )