github.com/trustbloc/kms-go@v1.1.2/secretlock/local/masterlock/pbkdf2/master_secret_lock.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package pbkdf2
     8  
     9  import (
    10  	"crypto/cipher"
    11  	"crypto/sha256"
    12  	"encoding/base64"
    13  	"fmt"
    14  	"hash"
    15  
    16  	"github.com/google/tink/go/subtle/random"
    17  	"golang.org/x/crypto/pbkdf2"
    18  
    19  	"github.com/trustbloc/kms-go/spi/secretlock"
    20  
    21  	cipherutil "github.com/trustbloc/kms-go/secretlock/local/internal/cipher"
    22  )
    23  
    24  // package pbkdf2 provides an pbkdf2 implementation of secretlock as a masterlock.
    25  // the underlying golang.org/x/crypto/pbkdf2 package implements IETF RFC 8018's PBKDF2 specification found at:
    26  // https://tools.ietf.org/html/rfc8018#section-5.2. Similarly the NIST document 800-132 section 5 provides PBKDF
    27  // recommendations.
    28  
    29  type masterLockPBKDF2 struct {
    30  	h    func() hash.Hash
    31  	salt []byte
    32  	aead cipher.AEAD
    33  }
    34  
    35  // NewMasterLock is responsible for encrypting/decrypting with a master key expanded from a passphrase using PBKDF2
    36  // using `passphrase`, hash function `h`, `salt`.
    37  // The salt is optional and can be set to nil.
    38  // This implementation must not be used directly in Aries framework. It should be passed in
    39  // as the second argument to local secret lock service constructor:
    40  // `local.NewService(masterKeyReader io.Reader, secLock secretlock.Service)`.
    41  func NewMasterLock(passphrase string, h func() hash.Hash, iterations int, salt []byte) (secretlock.Service, error) {
    42  	if passphrase == "" {
    43  		return nil, fmt.Errorf("passphrase is empty")
    44  	}
    45  
    46  	if h == nil {
    47  		return nil, fmt.Errorf("hash is nil")
    48  	}
    49  
    50  	size := h().Size()
    51  	if size > sha256.Size { // AEAD cipher requires at most sha256.Size
    52  		return nil, fmt.Errorf("hash size not supported")
    53  	}
    54  
    55  	masterKey := pbkdf2.Key([]byte(passphrase), salt, iterations, sha256.Size, h)
    56  
    57  	aead, err := cipherutil.CreateAESCipher(masterKey)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	return &masterLockPBKDF2{
    63  		h:    h,
    64  		salt: salt,
    65  		aead: aead,
    66  	}, nil
    67  }
    68  
    69  // Encrypt a master key in req
    70  //
    71  //	(keyURI is used for remote locks, it is ignored by this implementation)
    72  func (m *masterLockPBKDF2) Encrypt(keyURI string, req *secretlock.EncryptRequest) (*secretlock.EncryptResponse, error) {
    73  	nonce := random.GetRandomBytes(uint32(m.aead.NonceSize()))
    74  	ct := m.aead.Seal(nil, nonce, []byte(req.Plaintext), []byte(req.AdditionalAuthenticatedData))
    75  	ct = append(nonce, ct...)
    76  
    77  	return &secretlock.EncryptResponse{
    78  		Ciphertext: base64.URLEncoding.EncodeToString(ct),
    79  	}, nil
    80  }
    81  
    82  // Decrypt a master key in req
    83  // (keyURI is used for remote locks, it is ignored by this implementation).
    84  func (m *masterLockPBKDF2) Decrypt(keyURI string, req *secretlock.DecryptRequest) (*secretlock.DecryptResponse, error) {
    85  	ct, err := base64.URLEncoding.DecodeString(req.Ciphertext)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	nonceSize := uint32(m.aead.NonceSize())
    91  
    92  	// ensure ciphertext contains more than nonce+ciphertext (result from Encrypt())
    93  	if len(ct) <= int(nonceSize) {
    94  		return nil, fmt.Errorf("invalid request")
    95  	}
    96  
    97  	nonce := ct[0:nonceSize]
    98  	ct = ct[nonceSize:]
    99  
   100  	pt, err := m.aead.Open(nil, nonce, ct, []byte(req.AdditionalAuthenticatedData))
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	return &secretlock.DecryptResponse{Plaintext: string(pt)}, nil
   106  }