github.com/tommi2day/gomodules@v1.13.2-0.20240423190010-b7d55d252a27/pwlib/gpg_test.go (about)

     1  package pwlib
     2  
     3  import (
     4  	"os"
     5  	"path"
     6  	"path/filepath"
     7  	"slices"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/tommi2day/gomodules/common"
    12  	"github.com/tommi2day/gomodules/test"
    13  
    14  	"github.com/ProtonMail/go-crypto/openpgp"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  const testGPGName = "Test User"
    21  const testGPGEmail = "test@example.com"
    22  const testGPGPass = "123456Pass!"
    23  
    24  func TestGPG(t *testing.T) {
    25  	var err error
    26  	var gpgid string
    27  	var keypass string
    28  	var key string
    29  	var entityList openpgp.EntityList
    30  	var entity *openpgp.Entity
    31  	test.Testinit(t)
    32  
    33  	secretGPGKeyFile := path.Join(test.TestData, "test.gpg.key")
    34  	publicGPGKeyFile := path.Join(test.TestData, "test.asc")
    35  	_ = os.Remove(publicGPGKeyFile)
    36  	_ = os.Remove(secretGPGKeyFile)
    37  
    38  	t.Run("GPG Gen Key", func(t *testing.T) {
    39  		keypass = testGPGPass
    40  		entity, gpgid, err = CreateGPGEntity(testGPGName, "TestCrypt", testGPGEmail, keypass)
    41  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
    42  		assert.NotNil(t, entity, "entity should not be nil")
    43  		if entity == nil {
    44  			t.Fatal("entity should not be nil")
    45  		}
    46  		assert.NotEmpty(t, gpgid, "KeyID should not be empty")
    47  		assert.True(t, entity.PrivateKey.Encrypted, "encrypted flag should be true")
    48  		err = ExportGPGKeyPair(entity, publicGPGKeyFile, secretGPGKeyFile)
    49  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
    50  		if err != nil {
    51  			t.Fatal("GPG keys not created as expected")
    52  		}
    53  		require.FileExists(t, publicGPGKeyFile)
    54  		content := ""
    55  		content, err = common.ReadFileToString(publicGPGKeyFile)
    56  		assert.NoErrorf(t, err, "File Read Error %s", err)
    57  		assert.Contains(t, content, "PGP PUBLIC KEY BLOCK")
    58  
    59  		require.FileExists(t, secretGPGKeyFile)
    60  		content, err = common.ReadFileToString(secretGPGKeyFile)
    61  		assert.NoErrorf(t, err, "File Read Error %s", err)
    62  		assert.Contains(t, content, "PGP PRIVATE KEY BLOCK")
    63  	})
    64  	if err != nil {
    65  		t.Fatal("GPG keys not created as expected")
    66  	}
    67  	t.Run("GPG Read Public Key", func(t *testing.T) {
    68  		key, err = common.ReadFileToString(publicGPGKeyFile)
    69  		entityList, err = GPGReadAmoredKeyRing(key)
    70  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
    71  		assert.NotNil(t, entityList, "should not be nil")
    72  	})
    73  
    74  	t.Run("GPGUnlockKey", func(t *testing.T) {
    75  		key, err = common.ReadFileToString(secretGPGKeyFile)
    76  		entityList, err = GPGReadAmoredKeyRing(key)
    77  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
    78  		assert.NotNil(t, entityList, "should not be nil")
    79  		if entityList == nil {
    80  			t.Fatal("entityList should not be nil")
    81  		}
    82  		entity, err = GPGSelectEntity(entityList, gpgid)
    83  		assert.NoErrorf(t, err, "select should be no error, but got %v", err)
    84  		assert.NotNil(t, entity, "entity should not be nil")
    85  		err = GPGUnlockKey(entity, keypass)
    86  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
    87  		if entity != nil {
    88  			assert.False(t, entity.PrivateKey.Encrypted, "encrypted flag should be false")
    89  		}
    90  	})
    91  	plaintextfile := path.Join(test.TestData, "test.gpg.txt")
    92  	//nolint gosec
    93  	err = os.WriteFile(plaintextfile, []byte(plain), 0644)
    94  	require.NoErrorf(t, err, "Create testdata failed")
    95  	cryptedfile := path.Join(test.TestData, "test.gpg.crypt")
    96  	t.Run("Encrypt GPG File", func(t *testing.T) {
    97  		err = GPGEncryptFile(plaintextfile, cryptedfile, publicGPGKeyFile)
    98  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
    99  	})
   100  	t.Run("Decrypt GPG File", func(t *testing.T) {
   101  		actual := ""
   102  		actual, err = GPGDecryptFile(cryptedfile, secretGPGKeyFile, keypass, "")
   103  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
   104  		assert.Equal(t, plain, actual, "should be equal")
   105  	})
   106  }
   107  
   108  func TestGopassSecrets(t *testing.T) {
   109  	var err error
   110  	var gpgid string
   111  	var keyPass string
   112  
   113  	test.Testinit(t)
   114  	secretGPGKeyFile := path.Join(test.TestDir, "gpg", "test.gpg.key")
   115  	// publicGPGKeyFile := path.Join(test.TestDir, "gpg", "test.asc")
   116  	gpgKeyPassFile := path.Join(test.TestDir, "gpg", "test.gpgpw")
   117  	storeRoot := path.Join(test.TestDir, "pwlib-store")
   118  	keyIDFile := path.Join(storeRoot, ".gpg-id")
   119  	gpgid, err = common.ReadFileToString(keyIDFile)
   120  	require.NoErrorf(t, err, "GetKeyId should be no error, but got %v", err)
   121  	keyPass, err = common.ReadFileToString(gpgKeyPassFile)
   122  	require.NoErrorf(t, err, "GetKeyPass should be no error, but got %v", err)
   123  	t.Run("Check GoPass Root OK", func(t *testing.T) {
   124  		actual, err := checkStoreRoot(storeRoot)
   125  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
   126  		assert.Equal(t, gpgid, actual, "KeyID not match")
   127  	})
   128  	t.Run("Check Gopass Root Err", func(t *testing.T) {
   129  		actual, err := checkStoreRoot(test.TestDir)
   130  		assert.Error(t, err, "should be error")
   131  		assert.Empty(t, actual, "should be empty")
   132  	})
   133  	t.Run("Find GoPass GPG Files", func(t *testing.T) {
   134  		// store name is not part of result
   135  		sr := filepath.ToSlash(filepath.Dir(storeRoot))
   136  		actual := findGPGFiles(storeRoot)
   137  		expected := []string{"pwlib-store/test/test1.gpg", "pwlib-store/test/test2.gpg", "pwlib-store/passphrase.gpg"}
   138  		assert.Equal(t, len(expected), len(actual), "len should be %d", len(expected))
   139  		t.Log(actual)
   140  		for _, e := range expected {
   141  			n := path.Join(sr, e)
   142  			found := slices.Contains(actual, n)
   143  			assert.True(t, found, "%s not found in result", n)
   144  		}
   145  	})
   146  
   147  	t.Run("List Gopass Secrets", func(t *testing.T) {
   148  		actual := ""
   149  		actual, err = GetGopassSecrets(storeRoot, secretGPGKeyFile, keyPass)
   150  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
   151  		if err != nil {
   152  			t.Fatal(err)
   153  		}
   154  		lines := strings.Split(actual, "\n")
   155  		expected := []string{"pwlib-store:passphrase:", "pwlib-store/test:test1:123456", "pwlib-store/test:test2:"}
   156  		assert.Equal(t, len(expected), len(lines), "len should be %d", len(expected))
   157  		t.Log(lines)
   158  		for _, e := range expected {
   159  			found := false
   160  			for _, l := range lines {
   161  				if strings.Contains(l, e) {
   162  					found = true
   163  					break
   164  				}
   165  			}
   166  			assert.True(t, found, "%s not found in result", e)
   167  		}
   168  	})
   169  	t.Run("Decrypt Gopass GPG File", func(t *testing.T) {
   170  		actual := ""
   171  		filename := path.Join(storeRoot, "test", "test1.gpg")
   172  		actual, err = GPGDecryptFile(filename, secretGPGKeyFile, keyPass, "")
   173  		assert.NoErrorf(t, err, "should be no error, but got %v", err)
   174  		assert.Equal(t, "123456\n", actual, "should be equal")
   175  	})
   176  	t.Run("GoPass GetPassword", func(t *testing.T) {
   177  		app := "test"
   178  		pass := ""
   179  		pc := NewConfig(app, storeRoot, path.Dir(secretGPGKeyFile), keyPass, typeGopass)
   180  		pc.PrivateKeyFile = secretGPGKeyFile
   181  		pass, err = pc.GetPassword("pwlib-store/test", "test1")
   182  		expected := "123456"
   183  		assert.NoErrorf(t, err, "Got unexpected error: %s", err)
   184  		assert.Equal(t, expected, pass, "Answer not expected. exp:%s,act:%s", expected, pass)
   185  	})
   186  }