Skip to content

Commit 9ca833a

Browse files
easyCZroboquat
authored andcommitted
[db] Support encrypted JSON in Go
1 parent 33b135f commit 9ca833a

File tree

9 files changed

+157
-25
lines changed

9 files changed

+157
-25
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package dbtest
6+
7+
import (
8+
"encoding/base64"
9+
db "github.com/gitpod-io/gitpod/components/gitpod-db/go"
10+
"github.com/stretchr/testify/require"
11+
"testing"
12+
)
13+
14+
func GetTestCipher(t *testing.T) (*db.AES256CBC, db.CipherMetadata) {
15+
t.Helper()
16+
17+
// This is a test key also used in server tests - see components/gitpod-protocol/src/encryption/encryption-engine.spec.ts
18+
key, err := base64.StdEncoding.DecodeString("ZMaTPrF7s9gkLbY45zP59O0LTpLvDd/cgqPE9Ptghh8=")
19+
require.NoError(t, err)
20+
21+
metadata := db.CipherMetadata{
22+
Name: "default",
23+
Version: 1,
24+
}
25+
cipher, err := db.NewAES256CBCCipher(string(key), metadata)
26+
require.NoError(t, err)
27+
return cipher, metadata
28+
}

components/gitpod-db/go/dbtest/oidc_client_config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ import (
1717
func NewOIDCClientConfig(t *testing.T, record db.OIDCClientConfig) db.OIDCClientConfig {
1818
t.Helper()
1919

20+
cipher, _ := GetTestCipher(t)
21+
encrypted, err := db.EncryptJSON(cipher, db.OIDCSpec{})
22+
require.NoError(t, err)
23+
2024
now := time.Now().UTC().Truncate(time.Millisecond)
2125
result := db.OIDCClientConfig{
2226
ID: uuid.New(),
2327
Issuer: "issuer",
24-
Data: []byte("{}"),
28+
Data: encrypted,
2529
LastModified: now,
2630
}
2731

components/gitpod-db/go/encryption.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@ import (
1414
"fmt"
1515
)
1616

17-
type Cipher interface {
17+
type Encryptor interface {
1818
Encrypt(data []byte) (EncryptedData, error)
19+
}
20+
21+
type Decryptor interface {
1922
Decrypt(data EncryptedData) ([]byte, error)
2023
}
2124

25+
type Cipher interface {
26+
Encryptor
27+
Decryptor
28+
}
29+
2230
func NewAES256CBCCipher(secret string, metadata CipherMetadata) (*AES256CBC, error) {
2331
block, err := aes.NewCipher([]byte(secret))
2432
if err != nil {
@@ -37,7 +45,7 @@ type AES256CBC struct {
3745
}
3846

3947
func (c *AES256CBC) Encrypt(data []byte) (EncryptedData, error) {
40-
iv, err := generateInitializationVector(16)
48+
iv, err := GenerateInitializationVector(16)
4149
if err != nil {
4250
return EncryptedData{}, err
4351
}
@@ -109,7 +117,7 @@ type EncryptedData struct {
109117
Metadata CipherMetadata `json:"keyMetadata"`
110118
}
111119

112-
func generateInitializationVector(size int) ([]byte, error) {
120+
func GenerateInitializationVector(size int) ([]byte, error) {
113121
buf := make([]byte, size)
114122
_, err := rand.Read(buf)
115123
if err != nil {

components/gitpod-db/go/encryption_test.go

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,26 @@
22
// Licensed under the GNU Affero General Public License (AGPL).
33
// See License.AGPL.txt in the project root for license information.
44

5-
package db
5+
package db_test
66

77
import (
88
"encoding/base64"
99
"fmt"
10+
db "github.com/gitpod-io/gitpod/components/gitpod-db/go"
11+
"github.com/gitpod-io/gitpod/components/gitpod-db/go/dbtest"
1012
"github.com/stretchr/testify/require"
1113
"testing"
1214
)
1315

1416
func TestAES256CBCCipher_Encrypt_Decrypt(t *testing.T) {
15-
secret, err := generateInitializationVector(32)
16-
require.NoError(t, err)
17+
secret := "testtesttesttesttesttesttesttest"
1718

18-
metadata := CipherMetadata{
19+
metadata := db.CipherMetadata{
1920
Name: "general",
2021
Version: 1,
2122
}
2223

23-
cipher, err := NewAES256CBCCipher(string(secret), metadata)
24+
cipher, err := db.NewAES256CBCCipher(secret, metadata)
2425
require.NoError(t, err)
2526

2627
data := []byte(`{ "foo": "bar", "another": "one" }`)
@@ -45,26 +46,15 @@ func TestAES256CBCCipher_Encrypt_Decrypt(t *testing.T) {
4546
}
4647

4748
func TestAES256CBCCipher_EncryptedByServer(t *testing.T) {
48-
// This is a test key also used in server tests - see components/gitpod-protocol/src/encryption/encryption-engine.spec.ts
49-
key, err := base64.StdEncoding.DecodeString("ZMaTPrF7s9gkLbY45zP59O0LTpLvDd/cgqPE9Ptghh8=")
50-
require.NoError(t, err)
51-
52-
metadata := CipherMetadata{
53-
Name: "general",
54-
Version: 1,
55-
}
56-
encrypted := EncryptedData{
57-
49+
cipher, metadata := dbtest.GetTestCipher(t)
50+
encrypted := db.EncryptedData{
5851
EncodedData: "YpgOY8ZNV64oG1DXiuCUXKy0thVySbN7uXTQxtC2j2A=",
59-
Params: KeyParams{
52+
Params: db.KeyParams{
6053
InitializationVector: "vpTOAFN5v4kOPsAHBKk+eg==",
6154
},
6255
Metadata: metadata,
6356
}
6457

65-
cipher, err := NewAES256CBCCipher(string(key), metadata)
66-
require.NoError(t, err)
67-
6858
decrypted, err := cipher.Decrypt(encrypted)
6959
fmt.Println(err)
7060
require.NoError(t, err)

components/gitpod-db/go/json.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package db
6+
7+
import (
8+
"encoding/json"
9+
"fmt"
10+
"gorm.io/datatypes"
11+
)
12+
13+
type EncryptedJSON[T any] datatypes.JSON
14+
15+
func (j *EncryptedJSON[T]) EncryptedData() (EncryptedData, error) {
16+
var data EncryptedData
17+
err := json.Unmarshal(*j, &data)
18+
if err != nil {
19+
return EncryptedData{}, fmt.Errorf("failed to unmarshal encrypted json: %w", err)
20+
}
21+
22+
return data, nil
23+
}
24+
25+
func (j *EncryptedJSON[T]) Decrypt(decryptor Decryptor) (T, error) {
26+
var out T
27+
data, err := j.EncryptedData()
28+
if err != nil {
29+
return out, fmt.Errorf("failed to obtain encrypted data: %w", err)
30+
}
31+
32+
b, err := decryptor.Decrypt(data)
33+
if err != nil {
34+
return out, fmt.Errorf("failed to decrypt encrypted json: %w", err)
35+
}
36+
37+
err = json.Unmarshal(b, &out)
38+
if err != nil {
39+
return out, fmt.Errorf("failed to unmarshal encrypted json: %w", err)
40+
}
41+
42+
return out, nil
43+
}
44+
45+
func EncryptJSON[T any](encryptor Encryptor, data T) (EncryptedJSON[T], error) {
46+
b, err := json.Marshal(data)
47+
if err != nil {
48+
return nil, fmt.Errorf("failed to marshal data into json: %w", err)
49+
}
50+
51+
encrypted, err := encryptor.Encrypt(b)
52+
if err != nil {
53+
return nil, fmt.Errorf("failed to encrypt json: %w", err)
54+
}
55+
56+
return NewEncryptedJSON[T](encrypted)
57+
}
58+
59+
func NewEncryptedJSON[T any](data EncryptedData) (EncryptedJSON[T], error) {
60+
b, err := json.Marshal(data)
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to serialize encrypted data into json: %w", err)
63+
}
64+
65+
return b, nil
66+
}

components/gitpod-db/go/json_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package db_test
6+
7+
import (
8+
db "github.com/gitpod-io/gitpod/components/gitpod-db/go"
9+
"github.com/gitpod-io/gitpod/components/gitpod-db/go/dbtest"
10+
"github.com/stretchr/testify/require"
11+
"testing"
12+
)
13+
14+
func TestEncryptJSON_DecryptJSON(t *testing.T) {
15+
cipher, _ := dbtest.GetTestCipher(t)
16+
17+
type Data struct {
18+
First string
19+
Second int
20+
}
21+
22+
data := Data{
23+
First: "first",
24+
Second: 2,
25+
}
26+
27+
encrypted, err := db.EncryptJSON(cipher, data)
28+
require.NoError(t, err)
29+
30+
decrypted, err := encrypted.Decrypt(cipher)
31+
require.NoError(t, err)
32+
33+
require.Equal(t, data, decrypted)
34+
}

components/gitpod-db/go/oidc_client_config.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"errors"
1010
"fmt"
1111
"github.com/google/uuid"
12-
"gorm.io/datatypes"
1312
"gorm.io/gorm"
1413
"time"
1514
)
@@ -19,7 +18,7 @@ type OIDCClientConfig struct {
1918

2019
Issuer string `gorm:"column:issuer;type:char;size:255;" json:"issuer"`
2120

22-
Data datatypes.JSON `gorm:"column:data;type:text;size:65535" json:"data"`
21+
Data EncryptedJSON[OIDCSpec] `gorm:"column:data;type:text;size:65535" json:"data"`
2322

2423
LastModified time.Time `gorm:"column:_lastModified;type:timestamp;default:CURRENT_TIMESTAMP(6);" json:"_lastModified"`
2524
// deleted is reserved for use by db-sync.
@@ -30,6 +29,9 @@ func (c *OIDCClientConfig) TableName() string {
3029
return "d_b_oidc_client_config"
3130
}
3231

32+
type OIDCSpec struct {
33+
}
34+
3335
func CreateOIDCCLientConfig(ctx context.Context, conn *gorm.DB, cfg OIDCClientConfig) (OIDCClientConfig, error) {
3436
if cfg.ID == uuid.Nil {
3537
return OIDCClientConfig{}, errors.New("OIDC Client Config ID must be set")
File renamed without changes.

0 commit comments

Comments
 (0)