-
Notifications
You must be signed in to change notification settings - Fork 64
/
crypt.go
153 lines (138 loc) Β· 3.48 KB
/
crypt.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package client
import (
"bytes"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"strings"
"time"
charm "github.com/charmbracelet/charm/proto"
"github.com/google/uuid"
"github.com/muesli/sasquatch"
)
// KeyForID returns the decrypted EncryptKey for a given key ID.
func (cc *Client) KeyForID(gid string) (*charm.EncryptKey, error) {
if len(cc.plainTextEncryptKeys) == 0 {
err := cc.cryptCheck()
if err != nil {
return nil, fmt.Errorf("failed crypt check: %w", err)
}
}
if gid == "" {
if len(cc.plainTextEncryptKeys) == 0 {
return nil, fmt.Errorf("No keys stored")
}
return cc.plainTextEncryptKeys[0], nil
}
for _, k := range cc.plainTextEncryptKeys {
if k.ID == gid {
return k, nil
}
}
return nil, fmt.Errorf("Key not found for id %s", gid)
}
// DefaultEncryptKey returns the default EncryptKey for an authed user.
func (cc *Client) DefaultEncryptKey() (*charm.EncryptKey, error) {
return cc.KeyForID("")
}
func (cc *Client) findIdentities() ([]sasquatch.Identity, error) {
keys, err := FindAuthKeys(cc.Config.Host, cc.Config.KeyType)
if err != nil {
return nil, err
}
var ids []sasquatch.Identity
for _, v := range keys {
id, err := sasquatch.ParseIdentitiesFile(v)
if err == nil {
ids = append(ids, id...)
}
}
return ids, nil
}
// EncryptKeys returns all of the symmetric encrypt keys for the authed user.
func (cc *Client) EncryptKeys() ([]*charm.EncryptKey, error) {
err := cc.cryptCheck()
if err != nil {
return nil, err
}
return cc.plainTextEncryptKeys, nil
}
func (cc *Client) addEncryptKey(pk string, gid string, key string, createdAt *time.Time) error {
buf := bytes.NewBuffer(nil)
r, err := sasquatch.ParseRecipient(pk)
if err != nil {
return err
}
w, err := sasquatch.Encrypt(buf, r)
if err != nil {
return err
}
w.Write([]byte(key))
w.Close()
encKey := base64.StdEncoding.EncodeToString(buf.Bytes())
ek := charm.EncryptKey{}
ek.PublicKey = pk
ek.ID = gid
ek.Key = encKey
ek.CreatedAt = createdAt
return cc.AuthedJSONRequest("POST", "/v1/encrypt-key", &ek, nil)
}
func (cc *Client) cryptCheck() error {
cc.encryptKeyLock.Lock()
defer cc.encryptKeyLock.Unlock()
auth, err := cc.Auth()
if err != nil {
return err
}
if len(auth.EncryptKeys) == 0 && len(cc.plainTextEncryptKeys) == 0 {
// if there are no encrypt keys, make one for the public key returned from auth
b := make([]byte, 64)
_, err := rand.Read(b)
if err != nil {
return err
}
k := base64.StdEncoding.EncodeToString(b)
ek := &charm.EncryptKey{}
ek.PublicKey = auth.PublicKey
ek.ID = uuid.New().String()
ek.Key = k
err = cc.addEncryptKey(ek.PublicKey, ek.ID, ek.Key, nil)
if err != nil {
return err
}
cc.plainTextEncryptKeys = []*charm.EncryptKey{ek}
return nil
}
if len(auth.EncryptKeys) > len(cc.plainTextEncryptKeys) {
// if the encryptKeys haven't been decrypted yet, use the sasquatch ids to decrypt them
sids, err := cc.findIdentities()
if err != nil {
return err
}
ks := make([]*charm.EncryptKey, 0)
for _, k := range auth.EncryptKeys {
ds, err := base64.StdEncoding.DecodeString(k.Key)
if err != nil {
return err
}
dr, err := sasquatch.Decrypt(bytes.NewReader(ds), sids...)
if err != nil {
return err
}
buf := new(strings.Builder)
_, err = io.Copy(buf, dr)
if err != nil {
return err
}
dk := &charm.EncryptKey{}
dk.Key = buf.String()
dk.PublicKey = k.PublicKey
dk.ID = k.ID
dk.CreatedAt = k.CreatedAt
ks = append(ks, dk)
}
cc.plainTextEncryptKeys = ks
}
return nil
}