Skip to content

Commit b69dd6e

Browse files
authored
Add support for remote encrypted databases (#138)
1 parent 85af5b9 commit b69dd6e

5 files changed

Lines changed: 57 additions & 34 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ go 1.20
44

55
require (
66
github.com/antlr4-go/antlr/v4 v4.13.0
7-
golang.org/x/sync v0.3.0
87
github.com/coder/websocket v1.8.12
8+
golang.org/x/sync v0.3.0
99
)
1010

1111
require golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,3 @@ golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88p
66
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
77
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
88
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
9-
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
10-
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=

libsql/internal/http/driver.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ import (
66
"github.com/tursodatabase/libsql-client-go/libsql/internal/http/hranaV2"
77
)
88

9-
func Connect(url, jwt, host string, schemaDb bool) driver.Conn {
10-
return hranaV2.Connect(url, jwt, host, schemaDb)
9+
func Connect(url, jwt, host string, schemaDb bool, remoteEncryptionKey string) driver.Conn {
10+
return hranaV2.Connect(url, jwt, host, schemaDb, remoteEncryptionKey)
1111
}

libsql/internal/http/hranaV2/hranaV2.go

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ import (
88
"encoding/json"
99
"errors"
1010
"fmt"
11-
"github.com/tursodatabase/libsql-client-go/sqliteparserutils"
1211
"io"
1312
"net/http"
1413
net_url "net/url"
1514
"runtime/debug"
1615
"strings"
1716

17+
"github.com/tursodatabase/libsql-client-go/sqliteparserutils"
18+
1819
"github.com/tursodatabase/libsql-client-go/libsql/internal/hrana"
1920
"github.com/tursodatabase/libsql-client-go/libsql/internal/http/shared"
2021
)
@@ -36,8 +37,8 @@ func init() {
3637
commitHash = "unknown"
3738
}
3839

39-
func Connect(url, jwt, host string, schemaDb bool) driver.Conn {
40-
return &hranaV2Conn{url, jwt, host, schemaDb, "", false, 0}
40+
func Connect(url, jwt, host string, schemaDb bool, encryptionKey string) driver.Conn {
41+
return &hranaV2Conn{url, jwt, host, schemaDb, encryptionKey, "", false, 0}
4142
}
4243

4344
type hranaV2Stmt struct {
@@ -82,13 +83,14 @@ func (s *hranaV2Stmt) QueryContext(ctx context.Context, args []driver.NamedValue
8283
}
8384

8485
type hranaV2Conn struct {
85-
url string
86-
jwt string
87-
host string
88-
schemaDb bool
89-
baton string
90-
streamClosed bool
91-
replicationIndex uint64
86+
url string
87+
jwt string
88+
host string
89+
schemaDb bool
90+
remoteEncryptionKey string
91+
baton string
92+
streamClosed bool
93+
replicationIndex uint64
9294
}
9395

9496
func (h *hranaV2Conn) Ping() error {
@@ -121,11 +123,11 @@ func (h *hranaV2Conn) PrepareContext(ctx context.Context, query string) (driver.
121123

122124
func (h *hranaV2Conn) Close() error {
123125
if h.baton != "" {
124-
go func(baton, url, jwt, host string) {
126+
go func(baton, url, jwt, host, encryptionKey string) {
125127
msg := hrana.PipelineRequest{Baton: baton}
126128
msg.Add(hrana.CloseStream())
127-
_, _, _ = sendPipelineRequest(context.Background(), &msg, url, jwt, host)
128-
}(h.baton, h.url, h.jwt, h.host)
129+
_, _, _ = sendPipelineRequest(context.Background(), &msg, url, jwt, host, encryptionKey)
130+
}(h.baton, h.url, h.jwt, h.host, h.remoteEncryptionKey)
129131
}
130132
return nil
131133
}
@@ -173,7 +175,7 @@ func (h *hranaV2Conn) sendPipelineRequest(ctx context.Context, msg *hrana.Pipeli
173175
if h.replicationIndex > 0 {
174176
addReplicationIndex(msg, h.replicationIndex)
175177
}
176-
result, streamClosed, err := sendPipelineRequest(ctx, msg, h.url, h.jwt, h.host)
178+
result, streamClosed, err := sendPipelineRequest(ctx, msg, h.url, h.jwt, h.host, h.remoteEncryptionKey)
177179
if streamClosed {
178180
h.streamClosed = true
179181
}
@@ -230,7 +232,7 @@ func getReplicationIndex(response *hrana.PipelineResponse) uint64 {
230232
return replicationIndex
231233
}
232234

233-
func sendPipelineRequest(ctx context.Context, msg *hrana.PipelineRequest, url string, jwt string, host string) (result hrana.PipelineResponse, streamClosed bool, err error) {
235+
func sendPipelineRequest(ctx context.Context, msg *hrana.PipelineRequest, url string, jwt string, host string, remoteEncryptionKey string) (result hrana.PipelineResponse, streamClosed bool, err error) {
234236
reqBody, err := json.Marshal(msg)
235237
if err != nil {
236238
return hrana.PipelineResponse{}, false, err
@@ -247,6 +249,10 @@ func sendPipelineRequest(ctx context.Context, msg *hrana.PipelineRequest, url st
247249
req.Header.Set("Authorization", "Bearer "+jwt)
248250
}
249251
req.Header.Set("x-libsql-client-version", "libsql-remote-go-"+commitHash)
252+
if remoteEncryptionKey != "" {
253+
req.Header.Set("x-turso-encryption-key", remoteEncryptionKey)
254+
}
255+
250256
req.Host = host
251257
resp, err := http.DefaultClient.Do(req)
252258
if err != nil {
@@ -591,11 +597,11 @@ func (h *hranaV2Conn) QueryContext(ctx context.Context, query string, args []dri
591597

592598
func (h *hranaV2Conn) closeStream() {
593599
if h.baton != "" {
594-
go func(baton, url, jwt, host string) {
600+
go func(baton, url, jwt, host, encryptionKey string) {
595601
msg := hrana.PipelineRequest{Baton: baton}
596602
msg.Add(hrana.CloseStream())
597-
_, _, _ = sendPipelineRequest(context.Background(), &msg, url, jwt, host)
598-
}(h.baton, h.url, h.jwt, h.host)
603+
_, _, _ = sendPipelineRequest(context.Background(), &msg, url, jwt, host, encryptionKey)
604+
}(h.baton, h.url, h.jwt, h.host, h.remoteEncryptionKey)
599605
h.baton = ""
600606
}
601607
}

libsql/sql.go

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import (
1414
)
1515

1616
type config struct {
17-
authToken *string
18-
tls *bool
19-
proxy *string
20-
schemaDb *bool
17+
authToken *string
18+
tls *bool
19+
proxy *string
20+
schemaDb *bool
21+
remoteEncryptionKey *string
2122
}
2223

2324
type Option interface {
@@ -76,6 +77,19 @@ func WithSchemaDb(schemaDb bool) Option {
7677
})
7778
}
7879

80+
func WithRemoteEncryptionKey(key string) Option {
81+
return option(func(o *config) error {
82+
if o.remoteEncryptionKey != nil {
83+
return fmt.Errorf("remoteEncryptionKey already set")
84+
}
85+
if key == "" {
86+
return fmt.Errorf("remoteEncryptionKey must not be empty")
87+
}
88+
o.remoteEncryptionKey = &key
89+
return nil
90+
})
91+
}
92+
7993
func (c config) connector(dbPath string) (driver.Connector, error) {
8094
u, err := url.Parse(dbPath)
8195
if err != nil {
@@ -139,6 +153,10 @@ func (c config) connector(dbPath string) (driver.Connector, error) {
139153
if c.authToken != nil {
140154
authToken = *c.authToken
141155
}
156+
encryptionKey := ""
157+
if c.remoteEncryptionKey != nil {
158+
encryptionKey = *c.remoteEncryptionKey
159+
}
142160

143161
host := u.Host
144162
if c.proxy != nil {
@@ -164,7 +182,7 @@ func (c config) connector(dbPath string) (driver.Connector, error) {
164182
return wsConnector{url: u.String(), authToken: authToken}, nil
165183
}
166184
if u.Scheme == "https" || u.Scheme == "http" {
167-
return httpConnector{url: u.String(), authToken: authToken, host: host, schemaDb: schemaDb}, nil
185+
return httpConnector{url: u.String(), authToken: authToken, host: host, schemaDb: schemaDb, remoteEncryptionKey: encryptionKey}, nil
168186
}
169187

170188
return nil, fmt.Errorf("unsupported URL scheme: %s\nThis driver supports only URLs that start with libsql://, file://, https://, http://, wss:// and ws://", u.Scheme)
@@ -185,14 +203,15 @@ func NewConnector(dbPath string, opts ...Option) (driver.Connector, error) {
185203
}
186204

187205
type httpConnector struct {
188-
url string
189-
authToken string
190-
host string
191-
schemaDb bool
206+
url string
207+
authToken string
208+
host string
209+
schemaDb bool
210+
remoteEncryptionKey string
192211
}
193212

194213
func (c httpConnector) Connect(_ctx context.Context) (driver.Conn, error) {
195-
return http.Connect(c.url, c.authToken, c.host, c.schemaDb), nil
214+
return http.Connect(c.url, c.authToken, c.host, c.schemaDb, c.remoteEncryptionKey), nil
196215
}
197216

198217
func (c httpConnector) Driver() driver.Driver {
@@ -341,7 +360,7 @@ func (d Driver) Open(dbUrl string) (driver.Conn, error) {
341360
return ws.Connect(u.String(), jwt)
342361
}
343362
if u.Scheme == "https" || u.Scheme == "http" {
344-
return http.Connect(u.String(), jwt, u.Host, false), nil
363+
return http.Connect(u.String(), jwt, u.Host, false, ""), nil
345364
}
346365

347366
return nil, fmt.Errorf("unsupported URL scheme: %s\nThis driver supports only URLs that start with libsql://, file://, https://, http://, wss:// and ws://", u.Scheme)

0 commit comments

Comments
 (0)