github.com/trustbloc/kms-go@v1.1.2/crypto/tinkcrypto/primitive/composite/register_ecdh_aead_enc_helper.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package composite
     8  
     9  import (
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  
    14  	aead "github.com/google/tink/go/aead/subtle"
    15  	"github.com/google/tink/go/core/registry"
    16  	gcmpb "github.com/google/tink/go/proto/aes_gcm_go_proto"
    17  	chachapb "github.com/google/tink/go/proto/chacha20_poly1305_go_proto"
    18  	tinkpb "github.com/google/tink/go/proto/tink_go_proto"
    19  	xchachapb "github.com/google/tink/go/proto/xchacha20_poly1305_go_proto"
    20  	"github.com/google/tink/go/tink"
    21  	"golang.org/x/crypto/chacha20poly1305"
    22  	"golang.org/x/crypto/poly1305"
    23  	"google.golang.org/protobuf/proto"
    24  
    25  	"github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/aead/subtle"
    26  	cbchmacpb "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/proto/aes_cbc_hmac_aead_go_proto"
    27  )
    28  
    29  const (
    30  	// AESCBCHMACAEADTypeURL for AESCBC+HMAC AEAD content encryption URL.
    31  	AESCBCHMACAEADTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.AesCbcHmacAeadKey"
    32  	// AESGCMTypeURL for AESGCM content encryption URL identifier.
    33  	AESGCMTypeURL = "type.googleapis.com/google.crypto.tink.AesGcmKey"
    34  	// ChaCha20Poly1305TypeURL for Chacha20Poly1305 content encryption URL identifier.
    35  	ChaCha20Poly1305TypeURL = "type.googleapis.com/google.crypto.tink.ChaCha20Poly1305Key"
    36  	// XChaCha20Poly1305TypeURL for XChachaPoly1305 content encryption URL identifier.
    37  	XChaCha20Poly1305TypeURL = "type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key"
    38  )
    39  
    40  type marshalFunc func(interface{}) ([]byte, error)
    41  
    42  // RegisterCompositeAEADEncHelper registers a content encryption helper.
    43  type RegisterCompositeAEADEncHelper struct {
    44  	encKeyURL   string
    45  	keyData     []byte
    46  	tagSize     int
    47  	ivSize      int
    48  	marshalFunc marshalFunc
    49  }
    50  
    51  var _ EncrypterHelper = (*RegisterCompositeAEADEncHelper)(nil)
    52  
    53  // NewRegisterCompositeAEADEncHelper initializes and returns a RegisterCompositeAEADEncHelper.
    54  //
    55  //nolint:gocyclo
    56  func NewRegisterCompositeAEADEncHelper(k *tinkpb.KeyTemplate) (*RegisterCompositeAEADEncHelper, error) {
    57  	var (
    58  		tagSize, ivSize int
    59  		skf             []byte
    60  		err             error
    61  	)
    62  
    63  	switch k.TypeUrl {
    64  	case AESCBCHMACAEADTypeURL:
    65  		cbcHMACKeyFormat := new(cbchmacpb.AesCbcHmacAeadKeyFormat)
    66  
    67  		err = proto.Unmarshal(k.Value, cbcHMACKeyFormat)
    68  		if err != nil {
    69  			return nil, fmt.Errorf("compositeAEADEncHelper: failed to unmarshal cbcHMACKeyFormat: %w", err)
    70  		}
    71  
    72  		tagSize = int(cbcHMACKeyFormat.HmacKeyFormat.Params.TagSize)
    73  		ivSize = subtle.AESCBCIVSize
    74  
    75  		skf, err = proto.Marshal(cbcHMACKeyFormat)
    76  		if err != nil {
    77  			return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize cbcHMAC key format, error: %w", err)
    78  		}
    79  	case AESGCMTypeURL:
    80  		gcmKeyFormat := new(gcmpb.AesGcmKeyFormat)
    81  
    82  		err = proto.Unmarshal(k.Value, gcmKeyFormat)
    83  		if err != nil {
    84  			return nil, fmt.Errorf("compositeAEADEncHelper: failed to unmarshal gcmKeyFormat: %w", err)
    85  		}
    86  
    87  		tagSize = aead.AESGCMTagSize
    88  		ivSize = aead.AESGCMIVSize
    89  
    90  		skf, err = proto.Marshal(gcmKeyFormat)
    91  		if err != nil {
    92  			return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize gcm key format, error: %w", err)
    93  		}
    94  	case ChaCha20Poly1305TypeURL:
    95  		tagSize = poly1305.TagSize
    96  		ivSize = chacha20poly1305.NonceSize
    97  
    98  		skf, err = buildChachaSKF(k)
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  	case XChaCha20Poly1305TypeURL:
   103  		tagSize = poly1305.TagSize
   104  		ivSize = chacha20poly1305.NonceSizeX
   105  
   106  		skf, err = buildXChachaSKF(k)
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  	default:
   111  		return nil, fmt.Errorf("compositeAEADEncHelper: unsupported AEAD content encryption key type: %s",
   112  			k.TypeUrl)
   113  	}
   114  
   115  	return buildRegisterCompositeAEADEncHelper(k, skf, tagSize, ivSize)
   116  }
   117  
   118  func buildChachaSKF(k *tinkpb.KeyTemplate) ([]byte, error) {
   119  	chachaKeyFormat := new(chachapb.ChaCha20Poly1305KeyFormat)
   120  
   121  	err := proto.Unmarshal(k.Value, chachaKeyFormat)
   122  	if err != nil {
   123  		return nil, fmt.Errorf("compositeAEADEncHelper: failed to unmarshal chachaKeyFormat: %w", err)
   124  	}
   125  
   126  	skf, err := proto.Marshal(chachaKeyFormat)
   127  	if err != nil {
   128  		return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize chacha key format, error: %w", err)
   129  	}
   130  
   131  	return skf, nil
   132  }
   133  
   134  func buildXChachaSKF(k *tinkpb.KeyTemplate) ([]byte, error) {
   135  	xChachaKeyFormat := new(xchachapb.XChaCha20Poly1305KeyFormat)
   136  
   137  	err := proto.Unmarshal(k.Value, xChachaKeyFormat)
   138  	if err != nil {
   139  		return nil, fmt.Errorf("compositeAEADEncHelper: failed to unmarshal xChachaKeyFormat: %w", err)
   140  	}
   141  
   142  	skf, err := proto.Marshal(xChachaKeyFormat)
   143  	if err != nil {
   144  		return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize xChacha key format, error: %w", err)
   145  	}
   146  
   147  	return skf, nil
   148  }
   149  
   150  func buildRegisterCompositeAEADEncHelper(k *tinkpb.KeyTemplate, skf []byte,
   151  	tagSize, ivSize int) (*RegisterCompositeAEADEncHelper, error) {
   152  	km, err := registry.GetKeyManager(k.TypeUrl)
   153  	if err != nil {
   154  		return nil, fmt.Errorf("compositeAEADEncHelper: failed to fetch KeyManager, error: %w", err)
   155  	}
   156  
   157  	key, err := km.NewKey(skf)
   158  	if err != nil {
   159  		return nil, fmt.Errorf("compositeAEADEncHelper: failed to fetch key, error: %w", err)
   160  	}
   161  
   162  	sk, err := proto.Marshal(key)
   163  	if err != nil {
   164  		return nil, fmt.Errorf("compositeAEADEncHelper: failed to serialize key, error: %w", err)
   165  	}
   166  
   167  	return &RegisterCompositeAEADEncHelper{
   168  		encKeyURL:   k.TypeUrl,
   169  		keyData:     sk,
   170  		tagSize:     tagSize,
   171  		ivSize:      ivSize,
   172  		marshalFunc: json.Marshal,
   173  	}, nil
   174  }
   175  
   176  // GetTagSize returns the primitive tag size.
   177  func (r *RegisterCompositeAEADEncHelper) GetTagSize() int {
   178  	return r.tagSize
   179  }
   180  
   181  // GetIVSize returns the primitive IV size.
   182  func (r *RegisterCompositeAEADEncHelper) GetIVSize() int {
   183  	return r.ivSize
   184  }
   185  
   186  // GetAEAD returns the AEAD primitive from the DEM.
   187  func (r *RegisterCompositeAEADEncHelper) GetAEAD(symmetricKeyValue []byte) (tink.AEAD, error) {
   188  	sk, err := r.getSerializedKey(symmetricKeyValue)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	p, err := registry.Primitive(r.encKeyURL, sk)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	g, ok := p.(tink.AEAD)
   199  	if !ok {
   200  		return nil, fmt.Errorf("invalid primitive")
   201  	}
   202  
   203  	return g, nil
   204  }
   205  
   206  func (r *RegisterCompositeAEADEncHelper) getSerializedKey(symmetricKeyValue []byte) ([]byte, error) { //nolint:gocyclo
   207  	var (
   208  		sk  []byte
   209  		err error
   210  	)
   211  
   212  	switch r.encKeyURL {
   213  	case AESCBCHMACAEADTypeURL:
   214  		sk, err = r.getSerializedAESCBCHMACKey(symmetricKeyValue)
   215  		if err != nil {
   216  			return nil, fmt.Errorf("registerCompositeAEADEncHelper: failed to serialize key, error: %w", err)
   217  		}
   218  	case AESGCMTypeURL:
   219  		sk, err = r.getSerializedAESGCMKey(symmetricKeyValue)
   220  		if err != nil {
   221  			return nil, fmt.Errorf("registerCompositeAEADEncHelper: failed to serialize key, error: %w", err)
   222  		}
   223  	case ChaCha20Poly1305TypeURL:
   224  		chachaKey := new(chachapb.ChaCha20Poly1305Key)
   225  
   226  		err = proto.Unmarshal(r.keyData, chachaKey)
   227  		if err != nil {
   228  			return nil, fmt.Errorf("registerCompositeAEADEncHelper: failed to unmarshal chacha key: %w", err)
   229  		}
   230  
   231  		chachaKey.KeyValue = symmetricKeyValue
   232  
   233  		sk, err = proto.Marshal(chachaKey)
   234  		if err != nil {
   235  			return nil, fmt.Errorf("registerCompositeAEADEncHelper: failed to serialize key, error: %w", err)
   236  		}
   237  	case XChaCha20Poly1305TypeURL:
   238  		xChachaKey := new(xchachapb.XChaCha20Poly1305Key)
   239  
   240  		err = proto.Unmarshal(r.keyData, xChachaKey)
   241  		if err != nil {
   242  			return nil, fmt.Errorf("registerCompositeAEADEncHelper: failed to unmarshal xchacha key: %w", err)
   243  		}
   244  
   245  		xChachaKey.KeyValue = symmetricKeyValue
   246  
   247  		sk, err = proto.Marshal(xChachaKey)
   248  		if err != nil {
   249  			return nil, fmt.Errorf("registerCompositeAEADEncHelper: failed to serialize key, error: %w", err)
   250  		}
   251  	default:
   252  		return nil, fmt.Errorf("registerCompositeAEADEncHelper: unsupported AEAD content encryption key type: %s",
   253  			r.encKeyURL)
   254  	}
   255  
   256  	return sk, err
   257  }
   258  
   259  func (r *RegisterCompositeAEADEncHelper) getSerializedAESGCMKey(symmetricKeyValue []byte) ([]byte, error) {
   260  	gcmKey := new(gcmpb.AesGcmKey)
   261  
   262  	err := proto.Unmarshal(r.keyData, gcmKey)
   263  	if err != nil {
   264  		return nil, fmt.Errorf("failed to unmarshal gcmKeyFormat: %w", err)
   265  	}
   266  
   267  	gcmKey.KeyValue = symmetricKeyValue
   268  
   269  	return proto.Marshal(gcmKey)
   270  }
   271  
   272  func (r *RegisterCompositeAEADEncHelper) getSerializedAESCBCHMACKey(symmetricKeyValue []byte) ([]byte, error) {
   273  	cbcHMACKey := new(cbchmacpb.AesCbcHmacAeadKey)
   274  
   275  	err := proto.Unmarshal(r.keyData, cbcHMACKey)
   276  	if err != nil {
   277  		return nil, fmt.Errorf("failed to unmarshal cbcHMACKeyFormat: %w", err)
   278  	}
   279  
   280  	var (
   281  		keySize int
   282  		twoKeys = 2
   283  	)
   284  
   285  	switch len(symmetricKeyValue) {
   286  	case subtle.AES128Size * twoKeys:
   287  		keySize = subtle.AES128Size
   288  	case subtle.AES192Size * twoKeys:
   289  		keySize = subtle.AES192Size
   290  	case subtle.AES256Size + subtle.AES192Size:
   291  		keySize = subtle.AES256Size
   292  	case subtle.AES256Size * twoKeys:
   293  		keySize = subtle.AES256Size
   294  	default:
   295  		return nil, errors.New("AES-CBC+HMAC-SHA key must be either 32, 48, 56 or 64 bytes")
   296  	}
   297  
   298  	cbcHMACKey.HmacKey.KeyValue = symmetricKeyValue[:keySize]
   299  	cbcHMACKey.AesCbcKey.KeyValue = symmetricKeyValue[keySize:]
   300  
   301  	return proto.Marshal(cbcHMACKey)
   302  }
   303  
   304  // BuildEncData will build the []byte representing the ciphertext sent to the end user as a result of the Composite
   305  // Encryption primitive execution.
   306  func (r *RegisterCompositeAEADEncHelper) BuildEncData(ct []byte) ([]byte, error) {
   307  	tagSize := r.GetTagSize()
   308  	ivSize := r.GetIVSize()
   309  	iv := ct[:ivSize]
   310  	ctAndTag := ct[ivSize:]
   311  	tagOffset := len(ctAndTag) - tagSize
   312  
   313  	encData := &EncryptedData{
   314  		Ciphertext: ctAndTag[:tagOffset],
   315  		IV:         iv,
   316  		Tag:        ctAndTag[tagOffset:],
   317  	}
   318  
   319  	return r.marshalFunc(encData)
   320  }
   321  
   322  // BuildDecData will build the []byte representing the ciphertext coming from encData struct returned as a result of
   323  // Composite Encrypt() call to prepare the Composite Decryption primitive execution.
   324  func (r *RegisterCompositeAEADEncHelper) BuildDecData(encData *EncryptedData) []byte {
   325  	iv := encData.IV
   326  	tag := encData.Tag
   327  	ct := encData.Ciphertext
   328  	finalCT := append(iv, ct...)
   329  	finalCT = append(finalCT, tag...)
   330  
   331  	return finalCT
   332  }