diff --git a/pkg/dbcrypt/dbcrypt.go b/pkg/dbcrypt/dbcrypt.go index 49b6e59..9e347fa 100644 --- a/pkg/dbcrypt/dbcrypt.go +++ b/pkg/dbcrypt/dbcrypt.go @@ -12,10 +12,11 @@ import ( "fmt" "io" "reflect" - - "github.com/greenbone/opensight-golang-libraries/pkg/dbcrypt/config" + "sync/atomic" "github.com/rs/zerolog/log" + + "github.com/greenbone/opensight-golang-libraries/pkg/dbcrypt/config" ) const ( @@ -24,18 +25,28 @@ const ( ) type DBCrypt[T any] struct { - config config.CryptoConfig + config atomic.Pointer[config.CryptoConfig] +} + +// New creates a new instance of DBCrypt that will perform cryptographic operations based on the provided CryptoConfig. +func New[T any](config config.CryptoConfig) *DBCrypt[T] { + d := &DBCrypt[T]{} + d.config.Store(&config) + return d } func (d *DBCrypt[T]) loadKey() []byte { - if d.config == (config.CryptoConfig{}) { + configPtr := d.config.Load() + if configPtr == nil { conf, err := config.Read() if err != nil { log.Fatal().Err(err).Msg("crypto config is invalid") } - d.config = conf + d.config.CompareAndSwap(nil, &conf) + configPtr = d.config.Load() } - key := []byte(d.config.ReportEncryptionV1Password + d.config.ReportEncryptionV1Salt)[:32] // Truncate key to 32 bytes + // TODO: Use proper KDF instead of truncating the password (to maintain backward compatibility use encrypted values prefixes to determine the method) + key := []byte(configPtr.ReportEncryptionV1Password + configPtr.ReportEncryptionV1Salt)[:32] // Truncate key to 32 bytes return key } diff --git a/pkg/dbcrypt/dbcrypt_test.go b/pkg/dbcrypt/dbcrypt_test.go index 67632e2..784b7f1 100644 --- a/pkg/dbcrypt/dbcrypt_test.go +++ b/pkg/dbcrypt/dbcrypt_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" + + "github.com/greenbone/opensight-golang-libraries/pkg/dbcrypt/config" ) type MyTable struct { @@ -111,3 +113,34 @@ func TestApplianceEncryption(t *testing.T) { myDB.First(&resultData, tblData.ID) assert.EqualValues(t, "thePasswordWhichCanBeEncrypted", resultData.PwdField) } + +func TestNew(t *testing.T) { + t.Setenv("TASK_REPORT_CRYPTO_V1_PASSWORD", "from-env-password") + t.Setenv("TASK_REPORT_CRYPTO_V1_SALT", "from-env-salt-012345678901234567") + + configCustom := config.CryptoConfig{ + ReportEncryptionV1Password: "custom-password", + ReportEncryptionV1Salt: "custom-salt-01234567890123456789", + } + dataClear := MyTable{ + Field1: "OtherField", + PwdField: "SecretPassword", + } + cryptDefault := DBCrypt[MyTable]{} + cryptCustom := New[MyTable](configCustom) + + dataEncrypted := dataClear + err := cryptCustom.EncryptStruct(&dataEncrypted) + require.NoError(t, err) + require.NotEqual(t, dataClear, dataEncrypted, "password was not encrypted") + + dataDefaultDecrypted := dataEncrypted + err = cryptDefault.DecryptStruct(&dataDefaultDecrypted) + require.Error(t, err) + assert.Equal(t, dataEncrypted, dataDefaultDecrypted) + + dataCustomDecrypted := dataEncrypted + err = cryptCustom.DecryptStruct(&dataCustomDecrypted) + require.NoError(t, err) + assert.Equal(t, dataClear, dataCustomDecrypted) +}