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

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  SPDX-License-Identifier: Apache-2.0
     4  */
     5  
     6  package local
     7  
     8  import (
     9  	"bytes"
    10  	"crypto/rand"
    11  	"crypto/sha256"
    12  	"encoding/base64"
    13  	"io/ioutil"
    14  	"os"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/google/tink/go/subtle/random"
    19  	"github.com/stretchr/testify/require"
    20  
    21  	"github.com/trustbloc/kms-go/spi/secretlock"
    22  
    23  	"github.com/trustbloc/kms-go/secretlock/local/masterlock/hkdf"
    24  	"github.com/trustbloc/kms-go/secretlock/local/masterlock/pbkdf2"
    25  )
    26  
    27  const (
    28  	testKeyURI = "test://test/key/uri"
    29  	envPrefix  = "TESTLOCAL_"
    30  )
    31  
    32  func TestCreateServiceFromPathWithDifferentFileSizes(t *testing.T) {
    33  	tcs := []struct {
    34  		tcName       string
    35  		fileName     string
    36  		masterKeyLen int
    37  		readerError  bool
    38  		serviceError bool
    39  		base64Enc    bool
    40  	}{
    41  		{
    42  			tcName:       "large file",
    43  			fileName:     "masterKey_file_large.txt",
    44  			masterKeyLen: 9999,
    45  			readerError:  false,
    46  			serviceError: true,
    47  		},
    48  		{
    49  			tcName:       "empty file",
    50  			fileName:     "masterKey_file_empty.txt",
    51  			masterKeyLen: 0,
    52  			readerError:  true,
    53  			serviceError: true,
    54  		},
    55  		{
    56  			tcName:       "valid file with raw master key content",
    57  			fileName:     "masterKey_file_valid_raw.txt",
    58  			masterKeyLen: 32,
    59  			readerError:  false,
    60  			serviceError: false,
    61  		},
    62  		{
    63  			tcName:       "valid file with base64 URL Encoded master key",
    64  			fileName:     "masterKey_file_valid_enc.txt",
    65  			masterKeyLen: 32,
    66  			readerError:  false,
    67  			serviceError: false,
    68  			base64Enc:    true,
    69  		},
    70  	}
    71  
    72  	for _, tc := range tcs {
    73  		t.Run(tc.tcName, func(t *testing.T) {
    74  			masterKeyFilePath := tc.fileName
    75  			tmpfile, err := ioutil.TempFile("", masterKeyFilePath)
    76  			require.NoError(t, err)
    77  
    78  			defer func() {
    79  				// close file
    80  				require.NoError(t, tmpfile.Close())
    81  				// clean up file
    82  				require.NoError(t, os.Remove(tmpfile.Name()))
    83  			}()
    84  
    85  			masterKeyContent := []byte{}
    86  
    87  			if tc.masterKeyLen != 0 {
    88  				masterKeyContent = random.GetRandomBytes(uint32(tc.masterKeyLen))
    89  				require.NotEmpty(t, masterKeyContent)
    90  			}
    91  
    92  			if tc.base64Enc {
    93  				keyEncoded := base64.URLEncoding.EncodeToString(masterKeyContent)
    94  				masterKeyContent = []byte(keyEncoded)
    95  			}
    96  
    97  			n, err := tmpfile.Write(masterKeyContent)
    98  			require.NoError(t, err)
    99  			require.Equal(t, len(masterKeyContent), n)
   100  
   101  			// try to get a reader
   102  			r, err := MasterKeyFromPath(tmpfile.Name())
   103  			if tc.readerError {
   104  				require.Error(t, err)
   105  				require.Empty(t, r)
   106  
   107  				// set r to empty reader
   108  				r = bytes.NewReader([]byte{})
   109  			} else {
   110  				require.NoError(t, err)
   111  				require.NotEmpty(t, r)
   112  			}
   113  
   114  			// try to create lock service for this above reader with nil master lock (master key not encrypted)
   115  			s, err := NewService(r, nil)
   116  			if tc.serviceError {
   117  				require.Error(t, err)
   118  				require.Empty(t, s)
   119  			} else {
   120  				require.NoError(t, err)
   121  				require.NotEmpty(t, s)
   122  			}
   123  		})
   124  	}
   125  }
   126  
   127  func TestCreateServiceFromPathWithoutMasterLock(t *testing.T) {
   128  	masterKeyFilePath := "masterKey_file.txt"
   129  	tmpfile, err := ioutil.TempFile("", masterKeyFilePath)
   130  	require.NoError(t, err)
   131  
   132  	defer func() {
   133  		// close file
   134  		require.NoError(t, tmpfile.Close())
   135  		// clean up file
   136  		require.NoError(t, os.Remove(tmpfile.Name()))
   137  	}()
   138  
   139  	masterKeyContent := random.GetRandomBytes(uint32(32))
   140  	require.NotEmpty(t, masterKeyContent)
   141  
   142  	masterKeyToSave := make([]byte, base64.URLEncoding.EncodedLen(len(masterKeyContent)))
   143  	base64.URLEncoding.Encode(masterKeyToSave, masterKeyContent)
   144  
   145  	n, err := tmpfile.Write(masterKeyToSave)
   146  	require.NoError(t, err)
   147  	require.Equal(t, len(masterKeyToSave), n)
   148  
   149  	// try invalid path
   150  	r, err := MasterKeyFromPath("bad/mk/test/file/name")
   151  	require.Error(t, err)
   152  	require.Empty(t, r)
   153  
   154  	// try real file path
   155  	r, err = MasterKeyFromPath(tmpfile.Name())
   156  	require.NoError(t, err)
   157  	require.NotEmpty(t, r)
   158  
   159  	// create lock service with nil master lock (master key not encrypted)
   160  	s, err := NewService(r, nil)
   161  	require.NoError(t, err)
   162  	require.NotEmpty(t, s)
   163  
   164  	someKey := random.GetRandomBytes(uint32(32))
   165  	someKeyEnc, err := s.Encrypt("", &secretlock.EncryptRequest{
   166  		Plaintext: string(someKey),
   167  	})
   168  	require.NoError(t, err)
   169  	require.NotEmpty(t, someKeyEnc)
   170  
   171  	someKeyDec, err := s.Decrypt("", &secretlock.DecryptRequest{
   172  		Ciphertext: someKeyEnc.Ciphertext,
   173  	})
   174  	require.NoError(t, err)
   175  	require.Equal(t, someKey, []byte(someKeyDec.Plaintext))
   176  
   177  	// try decrypting a non valid base64URL string
   178  	someKeyDec, err = s.Decrypt("", &secretlock.DecryptRequest{Ciphertext: "bad{}base64URLstring[]"})
   179  	require.Error(t, err)
   180  	require.Empty(t, someKeyDec)
   181  }
   182  
   183  func TestCreateServiceFromPathWithMasterLock(t *testing.T) {
   184  	// first create a master lock to use in our secret lock and encrypt the master key
   185  	passphrase := "secretPassphrase"
   186  	keySize := sha256.New().Size()
   187  	// salt is optional, it can be nil
   188  	salt := make([]byte, keySize)
   189  	_, err := rand.Read(salt)
   190  	require.NoError(t, err)
   191  
   192  	masterLockerHKDF, err := hkdf.NewMasterLock(passphrase, sha256.New, salt)
   193  	require.NoError(t, err)
   194  	require.NotEmpty(t, masterLockerHKDF)
   195  
   196  	masterLockerPBKDF2, err := pbkdf2.NewMasterLock(passphrase, sha256.New, 8192, salt)
   197  	require.NoError(t, err)
   198  	require.NotEmpty(t, masterLockerPBKDF2)
   199  
   200  	tests := []struct {
   201  		name       string
   202  		masterLock secretlock.Service
   203  	}{
   204  		{
   205  			name:       "lock using hkdf as masterlock",
   206  			masterLock: masterLockerHKDF,
   207  		}, {
   208  			name:       "lock using pbkdf2 as masterlock",
   209  			masterLock: masterLockerPBKDF2,
   210  		},
   211  	}
   212  
   213  	for _, tt := range tests {
   214  		tc := tt
   215  		t.Run("Test "+tc.name, func(t *testing.T) {
   216  			checkCreateServiceUsingMasterLock(t, tc.masterLock)
   217  		})
   218  	}
   219  }
   220  
   221  func checkCreateServiceUsingMasterLock(t *testing.T, masterLocker secretlock.Service) {
   222  	masterKeyFilePath := "masterKey_file.txt"
   223  	tmpfile, err := ioutil.TempFile("", masterKeyFilePath)
   224  	require.NoError(t, err)
   225  
   226  	defer func() {
   227  		// close file
   228  		require.NoError(t, tmpfile.Close())
   229  		// clean up file
   230  		require.NoError(t, os.Remove(tmpfile.Name()))
   231  	}()
   232  
   233  	masterKeyContent := random.GetRandomBytes(uint32(32))
   234  	require.NotEmpty(t, masterKeyContent)
   235  
   236  	// now encrypt masterKeyContent
   237  	masterLockEnc, err := masterLocker.Encrypt("", &secretlock.EncryptRequest{
   238  		Plaintext: string(masterKeyContent),
   239  	})
   240  	require.NoError(t, err)
   241  	require.NotEmpty(t, masterLockEnc)
   242  
   243  	// and write it to tmpfile
   244  	n, err := tmpfile.Write([]byte(masterLockEnc.Ciphertext))
   245  	require.NoError(t, err)
   246  	require.Equal(t, len(masterLockEnc.Ciphertext), n)
   247  
   248  	// now get a reader from path
   249  	r, err := MasterKeyFromPath(tmpfile.Name())
   250  	require.NoError(t, err)
   251  	require.NotEmpty(t, r)
   252  
   253  	// try a bad reader
   254  	badReader, err := MasterKeyFromPath("bad/mk/test/file/name")
   255  	require.Error(t, err)
   256  	require.Empty(t, badReader)
   257  
   258  	// finally create lock service with the master lock created earlier to encrypt decrypt keys using
   259  	// a protected (encrypted) master key
   260  	s, err := NewService(r, masterLocker)
   261  	require.NoError(t, err)
   262  	require.NotEmpty(t, s)
   263  
   264  	// now try to crate a lock service with a bad (nil) reader reference
   265  	badSerivce, err := NewService(badReader, masterLocker)
   266  	require.EqualError(t, err, "masterKeyReader is nil")
   267  	require.Empty(t, badSerivce)
   268  
   269  	// or a nil reader as argument
   270  	badSerivce, err = NewService(nil, masterLocker)
   271  	require.Error(t, err)
   272  	require.Empty(t, badSerivce)
   273  
   274  	// or a reader containing an invalid master key
   275  	badSerivce, err = NewService(bytes.NewReader([]byte("badMasterKey")), masterLocker)
   276  	require.Error(t, err)
   277  	require.Empty(t, badSerivce)
   278  
   279  	someKey := random.GetRandomBytes(uint32(32))
   280  	someKeyEnc, err := s.Encrypt("", &secretlock.EncryptRequest{
   281  		Plaintext: string(someKey),
   282  	})
   283  	require.NoError(t, err)
   284  	require.NotEmpty(t, someKeyEnc)
   285  
   286  	someKeyDec, err := s.Decrypt("", &secretlock.DecryptRequest{
   287  		Ciphertext: someKeyEnc.Ciphertext,
   288  	})
   289  	require.NoError(t, err)
   290  	require.Equal(t, someKey, []byte(someKeyDec.Plaintext))
   291  
   292  	// finally try to decrypt a bad ciphertext
   293  	badCipher := base64.URLEncoding.EncodeToString([]byte("BadCipherTextInAction"))
   294  
   295  	someKeyDec, err = s.Decrypt("", &secretlock.DecryptRequest{Ciphertext: badCipher})
   296  	require.Error(t, err)
   297  	require.Empty(t, someKeyDec)
   298  
   299  	// try with a short cipher (shorter than nonce+ciphertext)
   300  	badCipher = base64.URLEncoding.EncodeToString([]byte("short"))
   301  
   302  	someKeyDec, err = s.Decrypt("", &secretlock.DecryptRequest{Ciphertext: badCipher})
   303  	require.Error(t, err)
   304  	require.Empty(t, someKeyDec)
   305  }
   306  
   307  func TestCreateServiceFromEnvWithoutMasterLock(t *testing.T) {
   308  	masterKeyContent := random.GetRandomBytes(uint32(32))
   309  	require.NotEmpty(t, masterKeyContent)
   310  
   311  	envKey := envPrefix + strings.ReplaceAll(testKeyURI, "/", "_")
   312  
   313  	// set the master key (unencrypted) in env
   314  	err := os.Setenv(envKey, base64.URLEncoding.EncodeToString(masterKeyContent))
   315  	require.NoError(t, err)
   316  
   317  	defer func() {
   318  		// clean up env variable
   319  		require.NoError(t, os.Unsetenv(envKey))
   320  	}()
   321  
   322  	r, err := MasterKeyFromEnv(envPrefix, "bad/mk/test/key")
   323  	require.Error(t, err)
   324  	require.Empty(t, r)
   325  
   326  	r, err = MasterKeyFromEnv(envPrefix, testKeyURI)
   327  	require.NoError(t, err)
   328  	require.NotEmpty(t, r)
   329  
   330  	// create lock service with nil master lock (master key not encrypted)
   331  	s, err := NewService(r, nil)
   332  	require.NoError(t, err)
   333  	require.NotEmpty(t, s)
   334  
   335  	someKey := random.GetRandomBytes(uint32(32))
   336  	someKeyEnc, err := s.Encrypt("", &secretlock.EncryptRequest{
   337  		Plaintext: string(someKey),
   338  	})
   339  	require.NoError(t, err)
   340  	require.NotEmpty(t, someKeyEnc)
   341  
   342  	someKeyDec, err := s.Decrypt("", &secretlock.DecryptRequest{
   343  		Ciphertext: someKeyEnc.Ciphertext,
   344  	})
   345  	require.NoError(t, err)
   346  	require.Equal(t, someKey, []byte(someKeyDec.Plaintext))
   347  }
   348  
   349  func TestCreateServiceFromEnvWithMasterLock(t *testing.T) {
   350  	masterKeyContent := random.GetRandomBytes(uint32(32))
   351  	require.NotEmpty(t, masterKeyContent)
   352  
   353  	// first create a master lock to use in our secret lock and encrypt the master key
   354  	passphrase := "secretPassphrase"
   355  	keySize := sha256.New().Size()
   356  	// salt is optional, it can be nil
   357  	salt := make([]byte, keySize)
   358  	_, err := rand.Read(salt)
   359  	require.NoError(t, err)
   360  
   361  	masterLocker, err := hkdf.NewMasterLock(passphrase, sha256.New, salt)
   362  	require.NoError(t, err)
   363  	require.NotEmpty(t, masterLocker)
   364  
   365  	// now encrypt masterKeyContent
   366  	masterLockEnc, err := masterLocker.Encrypt("", &secretlock.EncryptRequest{
   367  		Plaintext: string(masterKeyContent),
   368  	})
   369  	require.NoError(t, err)
   370  	require.NotEmpty(t, masterLockEnc)
   371  
   372  	envKey := envPrefix + strings.ReplaceAll(testKeyURI, "/", "_")
   373  
   374  	// now set the encrypted master key in env
   375  	err = os.Setenv(envKey, masterLockEnc.Ciphertext)
   376  	require.NoError(t, err)
   377  
   378  	defer func() {
   379  		// clean up env variable
   380  		require.NoError(t, os.Unsetenv(envKey))
   381  	}()
   382  
   383  	// get a reader from an invalid env variable
   384  	badReader, err := MasterKeyFromEnv(envPrefix, "bad/mk/test/key")
   385  	require.Error(t, err)
   386  	require.Empty(t, badReader)
   387  
   388  	// get a reader from a valid env variable
   389  	r, err := MasterKeyFromEnv(envPrefix, testKeyURI)
   390  	require.NoError(t, err)
   391  	require.NotEmpty(t, r)
   392  
   393  	// finally create lock service with the master lock created earlier to encrypt decrypt keys using
   394  	// a protected (encrypted) master key
   395  	s, err := NewService(r, masterLocker)
   396  	require.NoError(t, err)
   397  	require.NotEmpty(t, s)
   398  
   399  	// now try to crate a lock service with a bad reader
   400  	badSerivce, err := NewService(badReader, masterLocker)
   401  	require.Error(t, err)
   402  	require.Empty(t, badSerivce)
   403  
   404  	// or a nil reader
   405  	badSerivce, err = NewService(nil, masterLocker)
   406  	require.Error(t, err)
   407  	require.Empty(t, badSerivce)
   408  
   409  	someKey := random.GetRandomBytes(uint32(32))
   410  	someKeyEnc, err := s.Encrypt("", &secretlock.EncryptRequest{
   411  		Plaintext: string(someKey),
   412  	})
   413  	require.NoError(t, err)
   414  	require.NotEmpty(t, someKeyEnc)
   415  
   416  	someKeyDec, err := s.Decrypt("", &secretlock.DecryptRequest{
   417  		Ciphertext: someKeyEnc.Ciphertext,
   418  	})
   419  	require.NoError(t, err)
   420  	require.Equal(t, someKey, []byte(someKeyDec.Plaintext))
   421  }