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

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  // Package tinkcrypto provides the default implementation of the
     8  // common pkg/common/api/crypto.Crypto interface and the SPI pkg/framework/aries.crypto interface
     9  //
    10  // It uses github.com/tink/go crypto primitives
    11  package tinkcrypto
    12  
    13  import (
    14  	"errors"
    15  	"fmt"
    16  
    17  	"github.com/google/tink/go/aead"
    18  	aeadsubtle "github.com/google/tink/go/aead/subtle"
    19  	"github.com/google/tink/go/core/primitiveset"
    20  	"github.com/google/tink/go/keyset"
    21  	"github.com/google/tink/go/mac"
    22  	"github.com/google/tink/go/signature"
    23  	"golang.org/x/crypto/chacha20poly1305"
    24  
    25  	"github.com/trustbloc/kms-go/spi/crypto"
    26  
    27  	"github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/aead/subtle"
    28  	"github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/bbs"
    29  )
    30  
    31  const (
    32  	// ECDHESA256KWAlg is the ECDH-ES with AES-GCM 256 key wrapping algorithm.
    33  	ECDHESA256KWAlg = "ECDH-ES+A256KW"
    34  	// ECDH1PUA128KWAlg is the ECDH-1PU with AES-CBC 128+HMAC-SHA 256 key wrapping algorithm.
    35  	ECDH1PUA128KWAlg = "ECDH-1PU+A128KW"
    36  	// ECDH1PUA192KWAlg is the ECDH-1PU with AES-CBC 192+HMAC-SHA 384 key wrapping algorithm.
    37  	ECDH1PUA192KWAlg = "ECDH-1PU+A192KW"
    38  	// ECDH1PUA256KWAlg is the ECDH-1PU with AES-CBC 256+HMAC-SHA 512 key wrapping algorithm.
    39  	ECDH1PUA256KWAlg = "ECDH-1PU+A256KW"
    40  	// ECDHESXC20PKWAlg is the ECDH-ES with XChacha20Poly1305 key wrapping algorithm.
    41  	ECDHESXC20PKWAlg = "ECDH-ES+XC20PKW"
    42  	// ECDH1PUXC20PKWAlg is the ECDH-1PU with XChacha20Poly1305 key wrapping algorithm.
    43  	ECDH1PUXC20PKWAlg = "ECDH-1PU+XC20PKW"
    44  
    45  	nistPECDHKWPrivateKeyTypeURL  = "type.hyperledger.org/hyperledger.aries.crypto.tink.NistPEcdhKwPrivateKey"
    46  	x25519ECDHKWPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.X25519EcdhKwPrivateKey"
    47  )
    48  
    49  var errBadKeyHandleFormat = errors.New("bad key handle format")
    50  
    51  // Package tinkcrypto includes the default implementation of pkg/crypto. It uses Tink for executing crypto primitives
    52  // and will be built as a framework option. It represents the main crypto service in the framework. `kh interface{}`
    53  // arguments in this implementation represent Tink's `*keyset.Handle`, using this type provides easy integration with
    54  // Tink and the default KMS service.
    55  
    56  // Crypto is the default Crypto SPI implementation using Tink.
    57  type Crypto struct {
    58  	ecKW  keyWrapper
    59  	okpKW keyWrapper
    60  }
    61  
    62  // New creates a new Crypto instance.
    63  func New() (*Crypto, error) {
    64  	return &Crypto{ecKW: &ecKWSupport{}, okpKW: &okpKWSupport{}}, nil
    65  }
    66  
    67  // Encrypt will encrypt msg using the implementation's corresponding encryption key and primitive in kh of a public key.
    68  func (t *Crypto) Encrypt(msg, aad []byte, kh interface{}) ([]byte, []byte, error) {
    69  	keyHandle, ok := kh.(*keyset.Handle)
    70  	if !ok {
    71  		return nil, nil, errBadKeyHandleFormat
    72  	}
    73  
    74  	ps, err := keyHandle.Primitives()
    75  	if err != nil {
    76  		return nil, nil, fmt.Errorf("get primitives: %w", err)
    77  	}
    78  
    79  	a, err := aead.New(keyHandle)
    80  	if err != nil {
    81  		return nil, nil, fmt.Errorf("create new aead: %w", err)
    82  	}
    83  
    84  	ct, err := a.Encrypt(msg, aad)
    85  	if err != nil {
    86  		return nil, nil, fmt.Errorf("encrypt msg: %w", err)
    87  	}
    88  
    89  	// Tink appends a key prefix + nonce to ciphertext, let's remove them to get the raw ciphertext
    90  	ivSize := nonceSize(ps)
    91  	prefixLength := len(ps.Primary.Prefix)
    92  	cipherText := ct[prefixLength+ivSize:]
    93  	nonce := ct[prefixLength : prefixLength+ivSize]
    94  
    95  	return cipherText, nonce, nil
    96  }
    97  
    98  func nonceSize(ps *primitiveset.PrimitiveSet) int {
    99  	var ivSize int
   100  	// AESGCM and XChacha20Poly1305 nonce sizes supported only for now
   101  	switch ps.Primary.Primitive.(type) {
   102  	case *aeadsubtle.XChaCha20Poly1305:
   103  		ivSize = chacha20poly1305.NonceSizeX
   104  	case *aeadsubtle.AESGCM:
   105  		ivSize = aeadsubtle.AESGCMIVSize
   106  	case *aeadsubtle.EncryptThenAuthenticate:
   107  		// AESCBC+HMACSHA Tink keys use Tink's EncryptThenAuthenticate AEAD primitive as per the CBC hmac key manager's
   108  		// Primitive() call.
   109  		ivSize = subtle.AES128Size
   110  	default:
   111  		ivSize = aeadsubtle.AESGCMIVSize
   112  	}
   113  
   114  	return ivSize
   115  }
   116  
   117  // Decrypt will decrypt cipher using the implementation's corresponding encryption key referenced by kh of
   118  // a private key.
   119  func (t *Crypto) Decrypt(cipher, aad, nonce []byte, kh interface{}) ([]byte, error) {
   120  	keyHandle, ok := kh.(*keyset.Handle)
   121  	if !ok {
   122  		return nil, errBadKeyHandleFormat
   123  	}
   124  
   125  	ps, err := keyHandle.Primitives()
   126  	if err != nil {
   127  		return nil, fmt.Errorf("get primitives: %w", err)
   128  	}
   129  
   130  	a, err := aead.New(keyHandle)
   131  	if err != nil {
   132  		return nil, fmt.Errorf("create new aead: %w", err)
   133  	}
   134  
   135  	for prefix := range ps.Entries {
   136  		// since Tink expects the key prefix + nonce as the ciphertext prefix, prepend them prior to calling its Decrypt()
   137  		ct := make([]byte, 0, len(prefix)+len(nonce)+len(cipher))
   138  		ct = append(ct, prefix...)
   139  		ct = append(ct, nonce...)
   140  		ct = append(ct, cipher...)
   141  
   142  		pt, e := a.Decrypt(ct, aad)
   143  
   144  		if e == nil {
   145  			return pt, nil
   146  		}
   147  	}
   148  
   149  	return nil, fmt.Errorf("decrypt cipher: decryption failed")
   150  }
   151  
   152  // Sign will sign msg using the implementation's corresponding signing key referenced by kh of a private key.
   153  func (t *Crypto) Sign(msg []byte, kh interface{}) ([]byte, error) {
   154  	keyHandle, ok := kh.(*keyset.Handle)
   155  	if !ok {
   156  		return nil, errBadKeyHandleFormat
   157  	}
   158  
   159  	signer, err := signature.NewSigner(keyHandle)
   160  	if err != nil {
   161  		return nil, fmt.Errorf("create new signer: %w", err)
   162  	}
   163  
   164  	s, err := signer.Sign(msg)
   165  	if err != nil {
   166  		return nil, fmt.Errorf("sign msg: %w", err)
   167  	}
   168  
   169  	return s, nil
   170  }
   171  
   172  // Verify will verify sig signature of msg using the implementation's corresponding signing key referenced by kh of
   173  // a public key.
   174  func (t *Crypto) Verify(sig, msg []byte, kh interface{}) error {
   175  	keyHandle, ok := kh.(*keyset.Handle)
   176  	if !ok {
   177  		return errBadKeyHandleFormat
   178  	}
   179  
   180  	verifier, err := signature.NewVerifier(keyHandle)
   181  	if err != nil {
   182  		return fmt.Errorf("create new verifier: %w", err)
   183  	}
   184  
   185  	err = verifier.Verify(sig, msg)
   186  	if err != nil {
   187  		err = fmt.Errorf("verify msg: %w", err)
   188  	}
   189  
   190  	return err
   191  }
   192  
   193  // ComputeMAC computes message authentication code (MAC) for code data
   194  // using a matching MAC primitive in kh key handle.
   195  func (t *Crypto) ComputeMAC(data []byte, kh interface{}) ([]byte, error) {
   196  	keyHandle, ok := kh.(*keyset.Handle)
   197  	if !ok {
   198  		return nil, errBadKeyHandleFormat
   199  	}
   200  
   201  	macPrimitive, err := mac.New(keyHandle)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	return macPrimitive.ComputeMAC(data)
   207  }
   208  
   209  // VerifyMAC determines if mac is a correct authentication code (MAC) for data
   210  // using a matching MAC primitive in kh key handle and returns nil if so, otherwise it returns an error.
   211  func (t *Crypto) VerifyMAC(macBytes, data []byte, kh interface{}) error {
   212  	keyHandle, ok := kh.(*keyset.Handle)
   213  	if !ok {
   214  		return errBadKeyHandleFormat
   215  	}
   216  
   217  	macPrimitive, err := mac.New(keyHandle)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	return macPrimitive.VerifyMAC(macBytes, data)
   223  }
   224  
   225  // WrapKey will do ECDH (ES or 1PU) key wrapping of cek using apu, apv and recipient public key 'recPubKey'.
   226  // This function is used with the following parameters:
   227  //   - Key Wrapping: `ECDH-ES` (no options) or `ECDH-1PU` (using crypto.WithSender() option in wrapKeyOpts) over either:
   228  //   - `ECDH-ES+A256KW` alg (AES256-GCM, default anoncrypt KW with no options) as per
   229  //     https://tools.ietf.org/html/rfc7518#appendix-A.2
   230  //   - `ECDH-ES+XC20PKW` alg (XChacha20Poly1305, anoncrypt using crypto.WithXC20PKW() option in wrapKeyOpts).
   231  //     The following ECDH-1PU algs are triggered using the crypto.WithSender() and crypto.WithTag() options in
   232  //     wrapKeyOpts:
   233  //   - `ECDH-1PU+A128KW` alg (AES128-GCM, authcrypt KW using cek size=32).
   234  //   - `ECDH-1PU+A192KW` alg (AES192-GCM, authcrypt KW using cek size=48).
   235  //   - `ECDH-1PU+A256KW` alg (AES256-GCM, authcrypt KW using cek size=64).
   236  //   - `ECDH-1PU+XC20PKW` alg (XChacha20Poly1305, authcrypt using crypto.WithXC20PKW() with cek size=32).
   237  //   - KDF (based on recPubKey.Curve):
   238  //     `Concat KDF` as per https://tools.ietf.org/html/rfc7518#section-4.6 (for recPubKey with NIST P curves) or
   239  //     `Curve25519`+`Concat KDF` as per https://tools.ietf.org/html/rfc7748#section-6.1
   240  //     (for recPubKey with X25519 curve).
   241  //
   242  // returns the resulting key wrapping info as *composite.RecipientWrappedKey or error in case of wrapping failure.
   243  func (t *Crypto) WrapKey(cek, apu, apv []byte, recPubKey *crypto.PublicKey,
   244  	wrapKeyOpts ...crypto.WrapKeyOpts) (*crypto.RecipientWrappedKey, error) {
   245  	if recPubKey == nil {
   246  		return nil, errors.New("wrapKey: recipient public key is required")
   247  	}
   248  
   249  	pOpts := crypto.NewOpt()
   250  
   251  	for _, opt := range wrapKeyOpts {
   252  		opt(pOpts)
   253  	}
   254  
   255  	wk, err := t.deriveKEKAndWrap(cek, apu, apv, pOpts.Tag(), pOpts.SenderKey(), recPubKey, pOpts.EPK(),
   256  		pOpts.UseXC20PKW())
   257  	if err != nil {
   258  		return nil, fmt.Errorf("wrapKey: %w", err)
   259  	}
   260  
   261  	return wk, nil
   262  }
   263  
   264  // UnwrapKey unwraps a key in recWK using ECDH (ES or 1PU) with recipient private key kh.
   265  // This function is used with the following parameters:
   266  //   - Key Unwrapping: `ECDH-ES` (no options) or `ECDH-1PU` (using crypto.WithSender() option in wrapKeyOpts)
   267  //     over either
   268  //   - `ECDH-ES+A256KW` alg (AES256-GCM, default anoncrypt KW with no options) as per
   269  //     https://tools.ietf.org/html/rfc7518#appendix-A.2
   270  //   - `ECDH-ES+XC20PKW` alg (XChacha20Poly1305, anoncrypt using crypto.WithXC20PKW() option in wrapKeyOpts).
   271  //     The following ECDH-1PU algs are triggered using the crypto.WithSender() and crypto.WithTag() options in
   272  //     wrapKeyOpts:
   273  //   - `ECDH-1PU+A128KW` alg (AES128-GCM, authcrypt KW using cek size=32).
   274  //   - `ECDH-1PU+A192KW` alg (AES192-GCM, authcrypt KW using cek size=48).
   275  //   - `ECDH-1PU+A256KW` alg (AES256-GCM, authcrypt KW using cek size=64).
   276  //   - `ECDH-1PU+XC20PKW` alg (XChacha20Poly1305, authcrypt using crypto.WithXC20PKW() with cek size=32).
   277  //   - KDF (based on recWk.EPK.KeyType): `Concat KDF` as per https://tools.ietf.org/html/rfc7518#section-4.6 (for type
   278  //     value as EC) or `Curve25519`+`Concat KDF` as per https://tools.ietf.org/html/rfc7748#section-6.1 (for type value
   279  //     as OKP, ie X25519 key).
   280  //
   281  // returns the resulting unwrapping key or error in case of unwrapping failure.
   282  //
   283  // Notes:
   284  // 1- if the crypto.WithSender() option was used in WrapKey(), then it must be set here as well for successful key
   285  //
   286  //	unwrapping.
   287  //
   288  // 2- unwrapping a key with recWK.alg value set as either `ECDH-1PU+A128KW`, `ECDH-1PU+A192KW`, `ECDH-1PU+A256KW` or
   289  //
   290  //	`ECDH-1PU+XC20PKW` requires the use of crypto.WithSender() option (containing the sender public key) in order to
   291  //	execute ECDH-1PU derivation.
   292  //
   293  // 3- the ephemeral key in recWK.EPK must have the same KeyType as the recipientKH and the same Curve for NIST P
   294  //
   295  //	curved keys. Unwrapping a key with non matching types/curves will result in unwrapping failure.
   296  //
   297  // 4- recipientKH must contain the private key since unwrapping is usually done on the recipient side.
   298  func (t *Crypto) UnwrapKey(recWK *crypto.RecipientWrappedKey, recipientKH interface{},
   299  	wrapKeyOpts ...crypto.WrapKeyOpts) ([]byte, error) {
   300  	if recWK == nil {
   301  		return nil, fmt.Errorf("unwrapKey: RecipientWrappedKey is empty")
   302  	}
   303  
   304  	pOpts := crypto.NewOpt()
   305  
   306  	for _, opt := range wrapKeyOpts {
   307  		opt(pOpts)
   308  	}
   309  
   310  	key, err := t.deriveKEKAndUnwrap(recWK.Alg, recWK.EncryptedCEK, recWK.APU, recWK.APV, pOpts.Tag(), &recWK.EPK,
   311  		pOpts.SenderKey(), recipientKH)
   312  	if err != nil {
   313  		return nil, fmt.Errorf("unwrapKey: %w", err)
   314  	}
   315  
   316  	return key, nil
   317  }
   318  
   319  // SignMulti will create a BBS+ signature of messages using the signer's private key in signerKH handle.
   320  // returns:
   321  //
   322  //	signature in []byte
   323  //	error in case of errors
   324  func (t *Crypto) SignMulti(messages [][]byte, signerKH interface{}) ([]byte, error) {
   325  	keyHandle, ok := signerKH.(*keyset.Handle)
   326  	if !ok {
   327  		return nil, errBadKeyHandleFormat
   328  	}
   329  
   330  	signer, err := bbs.NewSigner(keyHandle)
   331  	if err != nil {
   332  		return nil, fmt.Errorf("create new BBS+ signer: %w", err)
   333  	}
   334  
   335  	s, err := signer.Sign(messages)
   336  	if err != nil {
   337  		return nil, fmt.Errorf("BBS+ sign msg: %w", err)
   338  	}
   339  
   340  	return s, nil
   341  }
   342  
   343  // VerifyMulti will BBS+ verify a signature of messages against the signer's public key in signerPubKH handle.
   344  // returns:
   345  //
   346  //	error in case of errors or nil if signature verification was successful
   347  func (t *Crypto) VerifyMulti(messages [][]byte, bbsSignature []byte, signerPubKH interface{}) error {
   348  	keyHandle, ok := signerPubKH.(*keyset.Handle)
   349  	if !ok {
   350  		return errBadKeyHandleFormat
   351  	}
   352  
   353  	verifier, err := bbs.NewVerifier(keyHandle)
   354  	if err != nil {
   355  		return fmt.Errorf("create new BBS+ verifier: %w", err)
   356  	}
   357  
   358  	err = verifier.Verify(messages, bbsSignature)
   359  	if err != nil {
   360  		err = fmt.Errorf("BBS+ verify msg: %w", err)
   361  	}
   362  
   363  	return err
   364  }
   365  
   366  // VerifyProof will verify a BBS+ signature proof (generated e.g. by Verifier's DeriveProof() call) for revealedMessages
   367  // with the signer's public key in signerPubKH handle.
   368  // returns:
   369  //
   370  //	error in case of errors or nil if signature proof verification was successful
   371  func (t *Crypto) VerifyProof(revealedMessages [][]byte, proof, nonce []byte, signerPubKH interface{}) error {
   372  	keyHandle, ok := signerPubKH.(*keyset.Handle)
   373  	if !ok {
   374  		return errBadKeyHandleFormat
   375  	}
   376  
   377  	verifier, err := bbs.NewVerifier(keyHandle)
   378  	if err != nil {
   379  		return fmt.Errorf("create new BBS+ verifier: %w", err)
   380  	}
   381  
   382  	err = verifier.VerifyProof(revealedMessages, proof, nonce)
   383  	if err != nil {
   384  		err = fmt.Errorf("verify proof msg: %w", err)
   385  	}
   386  
   387  	return err
   388  }
   389  
   390  // DeriveProof will create a BBS+ signature proof for a list of revealed messages using BBS signature
   391  // (can be built using a Signer's SignMulti() call) and the signer's public key in signerPubKH handle.
   392  // returns:
   393  //
   394  //	signature proof in []byte
   395  //	error in case of errors
   396  func (t *Crypto) DeriveProof(messages [][]byte, bbsSignature, nonce []byte, revealedIndexes []int,
   397  	signerPubKH interface{}) ([]byte, error) {
   398  	keyHandle, ok := signerPubKH.(*keyset.Handle)
   399  	if !ok {
   400  		return nil, errBadKeyHandleFormat
   401  	}
   402  
   403  	verifier, err := bbs.NewVerifier(keyHandle)
   404  	if err != nil {
   405  		return nil, fmt.Errorf("create new BBS+ verifier: %w", err)
   406  	}
   407  
   408  	proof, err := verifier.DeriveProof(messages, bbsSignature, nonce, revealedIndexes)
   409  	if err != nil {
   410  		return nil, fmt.Errorf("verify proof msg: %w", err)
   411  	}
   412  
   413  	return proof, nil
   414  }