forked from stellar/go
-
Notifications
You must be signed in to change notification settings - Fork 1
/
keys.go
135 lines (118 loc) · 3.62 KB
/
keys.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package keystore
import (
"context"
"encoding/base64"
"encoding/json"
"time"
"github.com/lib/pq"
"github.com/stellar/go/support/errors"
"github.com/stellar/go/support/render/problem"
)
type encryptedKeysData struct {
KeysBlob string `json:"keysBlob"`
CreatedAt time.Time `json:"createdAt"`
ModifiedAt *time.Time `json:"modifiedAt,omitempty"`
}
type encryptedKeyData struct {
ID string `json:"id"`
Salt string `json:"salt"`
EncrypterName string `json:"encrypterName"`
EncryptedBlob string `json:"encryptedBlob"`
}
type putKeysRequest struct {
KeysBlob string `json:"keysBlob"`
}
func (s *Service) putKeys(ctx context.Context, in putKeysRequest) (*encryptedKeysData, error) {
userID := userID(ctx)
if userID == "" {
return nil, probNotAuthorized
}
if in.KeysBlob == "" {
return nil, problem.MakeInvalidFieldProblem("keysBlob", errRequiredField)
}
keysData, err := base64.RawURLEncoding.DecodeString(in.KeysBlob)
if err != nil {
// TODO: we need to implement a helper function in the
// support/error package for keeping the stack trace from err
// and substitute the root error for the one we want for better
// debugging experience.
// Thowing away the original err makes it harder for debugging.
return nil, probInvalidKeysBlob
}
var encryptedKeys []encryptedKeyData
err = json.Unmarshal(keysData, &encryptedKeys)
if err != nil {
return nil, probInvalidKeysBlob
}
for _, ek := range encryptedKeys {
if ek.Salt == "" {
return nil, problem.MakeInvalidFieldProblem("keysBlob", errors.New("salt is required for all the encrypted key data"))
}
if ek.EncrypterName == "" {
return nil, problem.MakeInvalidFieldProblem("keysBlob", errors.New("encrypterName is required for all the encrypted key data"))
}
if ek.EncryptedBlob == "" {
return nil, problem.MakeInvalidFieldProblem("keysBlob", errors.New("encryptedBlob is required for all the encrypted key data"))
}
if ek.ID == "" {
return nil, problem.MakeInvalidFieldProblem("keysBlob", errors.New("id is required for all the encrypted key data"))
}
}
q := `
INSERT INTO encrypted_keys (user_id, encrypted_keys_data)
VALUES ($1, $2)
ON CONFLICT (user_id) DO UPDATE SET encrypted_keys_data = excluded.encrypted_keys_data, modified_at = NOW()
RETURNING encrypted_keys_data, created_at, modified_at
`
var (
keysBlob []byte
out encryptedKeysData
modifiedAt pq.NullTime
)
err = s.db.QueryRowContext(ctx, q, userID, keysData).Scan(&keysBlob, &out.CreatedAt, &modifiedAt)
if err != nil {
return nil, errors.Wrap(err, "storing keys blob")
}
out.KeysBlob = base64.RawURLEncoding.EncodeToString(keysBlob)
if modifiedAt.Valid {
out.ModifiedAt = &modifiedAt.Time
}
return &out, nil
}
func (s *Service) getKeys(ctx context.Context) (*encryptedKeysData, error) {
userID := userID(ctx)
if userID == "" {
return nil, probNotAuthorized
}
q := `
SELECT encrypted_keys_data, created_at, modified_at
FROM encrypted_keys
WHERE user_id = $1
`
var (
keysBlob []byte
out encryptedKeysData
modifiedAt pq.NullTime
)
err := s.db.QueryRowContext(ctx, q, userID).Scan(&keysBlob, &out.CreatedAt, &modifiedAt)
if err != nil {
return nil, errors.Wrap(err, "getting keys blob")
}
out.KeysBlob = base64.RawURLEncoding.EncodeToString(keysBlob)
if modifiedAt.Valid {
out.ModifiedAt = &modifiedAt.Time
}
return &out, nil
}
func (s *Service) deleteKeys(ctx context.Context) error {
userID := userID(ctx)
if userID == "" {
return probNotAuthorized
}
q := `
DELETE FROM encrypted_keys
WHERE user_id = $1
`
_, err := s.db.ExecContext(ctx, q, userID)
return errors.Wrap(err, "deleting keys blob")
}