From 14dc0b0c22c41902a278663e3f6bec6bea0a4e06 Mon Sep 17 00:00:00 2001 From: francois samin Date: Wed, 3 Aug 2022 14:25:30 +0200 Subject: [PATCH 1/2] fix(engine): json encryption key rollover Signed-off-by: francois samin --- engine/gorpmapper/dao.go | 9 ++++-- engine/gorpmapper/encryption_test.go | 44 ++++++++++++++++++++++++++++ engine/gorpmapper/types.go | 13 +++++--- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/engine/gorpmapper/dao.go b/engine/gorpmapper/dao.go index 4c5bc1c39a..6b9d07210d 100644 --- a/engine/gorpmapper/dao.go +++ b/engine/gorpmapper/dao.go @@ -78,6 +78,10 @@ func (m *Mapper) UpdateColumns(db gorp.SqlExecutor, i interface{}, columnFilter hasPlaceHolder = true break } + if field.Kind() == reflect.Struct && field.IsZero() { + hasPlaceHolder = true + break + } } // If the data has encrypted data @@ -99,8 +103,9 @@ func (m *Mapper) UpdateColumns(db gorp.SqlExecutor, i interface{}, columnFilter for _, f := range mapping.EncryptedFields { // Reset the field to the decrypted value if the value is set to the placeholder field := val.FieldByName(f.Name) - if field.Interface() == sdk.PasswordPlaceholder { - oldVal := valTuple.FieldByName(f.Name) + oldVal := valTuple.FieldByName(f.Name) + + if field.Interface() == sdk.PasswordPlaceholder || field.Kind() == reflect.Struct && field.IsZero() { field.Set(oldVal) } } diff --git a/engine/gorpmapper/encryption_test.go b/engine/gorpmapper/encryption_test.go index a0c6d72179..5b34ac5613 100644 --- a/engine/gorpmapper/encryption_test.go +++ b/engine/gorpmapper/encryption_test.go @@ -23,11 +23,15 @@ func TestEncryption(t *testing.T) { Data: "data", SensitiveData: "sensitive-data", AnotherSensitiveData: "another-sensitive-data", + SensitiveJsonData: gorpmapper.SensitiveJsonData{ + Data: "some-data", + }, } require.NoError(t, m.InsertAndSign(context.TODO(), db, &d)) assert.Equal(t, sdk.PasswordPlaceholder, d.SensitiveData) assert.Equal(t, sdk.PasswordPlaceholder, d.AnotherSensitiveData) + assert.Zero(t, d.SensitiveJsonData) // UpdateAndSign should not save place holders require.NoError(t, m.UpdateAndSign(context.TODO(), db, &d)) @@ -82,6 +86,7 @@ func TestEncryption(t *testing.T) { require.Equal(t, d.Data, d2.Data) require.Equal(t, "sensitive--data", d2.SensitiveData) require.Equal(t, "another-sensitive-data", d2.AnotherSensitiveData) + assert.Equal(t, "some-data", d2.SensitiveJsonData.Data) } func TestEncryption_Multiple(t *testing.T) { @@ -94,6 +99,7 @@ func TestEncryption_Multiple(t *testing.T) { Data: "data-1", SensitiveData: "sensitive-data-1", AnotherSensitiveData: "another-sensitive-data-1", + SensitiveJsonData: gorpmapper.SensitiveJsonData{Data: "json-sentitive-data-1"}, } require.NoError(t, m.InsertAndSign(context.TODO(), db, &d1)) @@ -101,6 +107,7 @@ func TestEncryption_Multiple(t *testing.T) { Data: "data-2", SensitiveData: "sensitive-data-2", AnotherSensitiveData: "another-sensitive-data-2", + SensitiveJsonData: gorpmapper.SensitiveJsonData{Data: "json-sentitive-data-2"}, } require.NoError(t, m.InsertAndSign(context.TODO(), db, &d2)) @@ -114,11 +121,13 @@ func TestEncryption_Multiple(t *testing.T) { require.Equal(t, "data-1", dslice[0].Data) require.Equal(t, sdk.PasswordPlaceholder, dslice[0].SensitiveData) require.Equal(t, sdk.PasswordPlaceholder, dslice[0].AnotherSensitiveData) + require.Zero(t, dslice[0].SensitiveJsonData) require.Equal(t, d2.ID, dslice[1].ID) require.Equal(t, "data-2", dslice[1].Data) require.Equal(t, sdk.PasswordPlaceholder, dslice[1].SensitiveData) require.Equal(t, sdk.PasswordPlaceholder, dslice[1].AnotherSensitiveData) + require.Zero(t, dslice[1].SensitiveJsonData) // Test that GetAll replaces encrypted values with clearValue if WithDecryption options is used query = gorpmapper.NewQuery("SELECT * FROM test_encrypted_data WHERE id IN ($1, $2) ORDER BY id").Args(d1.ID, d2.ID) @@ -138,9 +147,44 @@ func TestEncryption_Multiple(t *testing.T) { require.Equal(t, "data-1", dslice[0].Data) require.Equal(t, "sensitive-data-1", dslice[0].SensitiveData) require.Equal(t, "another-sensitive-data-1", dslice[0].AnotherSensitiveData) + require.Equal(t, "json-sentitive-data-1", dslice[0].SensitiveJsonData.Data) require.Equal(t, d2.ID, dslice[1].ID) require.Equal(t, "data-2", dslice[1].Data) require.Equal(t, "sensitive-data-2", dslice[1].SensitiveData) require.Equal(t, "another-sensitive-data-2", dslice[1].AnotherSensitiveData) + require.Equal(t, "json-sentitive-data-2", dslice[1].SensitiveJsonData.Data) +} + +func TestRollEncryptedTupleByPrimaryKey(t *testing.T) { + m := gorpmapper.New() + m.Register(m.NewTableMapping(gorpmapper.TestEncryptedData{}, "test_encrypted_data", true, "id")) + + db, _ := test.SetupPGWithMapper(t, m, sdk.TypeAPI) + + var d1 = gorpmapper.TestEncryptedData{ + Data: "data-1", + SensitiveData: "sensitive-data-1", + AnotherSensitiveData: "another-sensitive-data-1", + SensitiveJsonData: gorpmapper.SensitiveJsonData{Data: "json-sentitive-data-1"}, + } + require.NoError(t, m.InsertAndSign(context.TODO(), db, &d1)) + require.NoError(t, m.UpdateAndSign(context.TODO(), db, &d1)) + + require.NoError(t, m.RollEncryptedTupleByPrimaryKey(context.Background(), db, "gorpmapper.TestEncryptedData", d1.ID)) + + var query = gorpmapper.NewQuery("select * from test_encrypted_data where id = $1").Args(d1.ID) + var d2 gorpmapper.TestEncryptedData + _, err := m.Get(context.TODO(), db, query, &d2, gorpmapping.GetOptions.WithDecryption) + require.NoError(t, err) + + isValid, err := m.CheckSignature(d2, d2.Signature) + require.NoError(t, err) + require.True(t, isValid) + + require.Equal(t, d1.ID, d2.ID) + require.Equal(t, d1.Data, d2.Data) + require.Equal(t, "sensitive-data-1", d2.SensitiveData) + require.Equal(t, "another-sensitive-data-1", d2.AnotherSensitiveData) + assert.Equal(t, "json-sentitive-data-1", d2.SensitiveJsonData.Data) } diff --git a/engine/gorpmapper/types.go b/engine/gorpmapper/types.go index a036b2abff..45c2352f2e 100644 --- a/engine/gorpmapper/types.go +++ b/engine/gorpmapper/types.go @@ -37,10 +37,15 @@ func (fs CanonicalForms) Latest() (*CanonicalForm, CanonicalForms) { type TestEncryptedData struct { SignedEntity - ID int64 `db:"id"` - Data string `db:"data"` - SensitiveData string `db:"sensitive_data" gorpmapping:"encrypted,Data"` - AnotherSensitiveData string `db:"another_sensitive_data" gorpmapping:"encrypted,ID,Data"` + ID int64 `db:"id"` + Data string `db:"data"` + SensitiveData string `db:"sensitive_data" gorpmapping:"encrypted,Data"` + AnotherSensitiveData string `db:"another_sensitive_data" gorpmapping:"encrypted,ID,Data"` + SensitiveJsonData SensitiveJsonData `db:"sensitive_json_data" gorpmapping:"encrypted,ID"` +} + +type SensitiveJsonData struct { + Data string } func (e TestEncryptedData) Canonical() CanonicalForms { From f9062ba62f77c060350f0b17f90787e461adc4a2 Mon Sep 17 00:00:00 2001 From: francois samin Date: Wed, 3 Aug 2022 14:25:48 +0200 Subject: [PATCH 2/2] fix(engine): json encryption key rollover Signed-off-by: francois samin --- engine/sql/api/247_database_encryption_test.sql | 5 +++++ engine/sql/cdn/017_database_encryption_test.sql | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 engine/sql/api/247_database_encryption_test.sql create mode 100644 engine/sql/cdn/017_database_encryption_test.sql diff --git a/engine/sql/api/247_database_encryption_test.sql b/engine/sql/api/247_database_encryption_test.sql new file mode 100644 index 0000000000..867be994e0 --- /dev/null +++ b/engine/sql/api/247_database_encryption_test.sql @@ -0,0 +1,5 @@ +-- +migrate Up +ALTER TABLE test_encrypted_data ADD COLUMN sensitive_json_data BYTEA; + +-- +migrate Down +ALTER TABLE test_encrypted_data DROP COLUMN sensitive_json_data; diff --git a/engine/sql/cdn/017_database_encryption_test.sql b/engine/sql/cdn/017_database_encryption_test.sql new file mode 100644 index 0000000000..867be994e0 --- /dev/null +++ b/engine/sql/cdn/017_database_encryption_test.sql @@ -0,0 +1,5 @@ +-- +migrate Up +ALTER TABLE test_encrypted_data ADD COLUMN sensitive_json_data BYTEA; + +-- +migrate Down +ALTER TABLE test_encrypted_data DROP COLUMN sensitive_json_data;