-
Notifications
You must be signed in to change notification settings - Fork 11
/
symmecrypt.go
278 lines (245 loc) · 7.7 KB
/
symmecrypt.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
package symmecrypt
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"sync"
)
var LogErrorFunc = log.Println
// Key is an abstraction of a symmetric encryption key
// - Encrypt / Decrypt provide low-level data encryption, with extra data for MAC
// - EncryptMarshal / DecryptMarshal build on top of that, working with a JSON representation of an object
// - Wait blocks until the Key is ready to be used (noop for the default implementation, useful for keys that need to be activated somehow)
type Key interface {
Encrypt([]byte, ...[]byte) ([]byte, error)
Decrypt([]byte, ...[]byte) ([]byte, error)
EncryptMarshal(interface{}, ...[]byte) (string, error)
DecryptMarshal(string, interface{}, ...[]byte) error
Wait()
String() (string, error)
}
// A KeyFactory instantiates a Key
type KeyFactory interface {
NewKey(string) (Key, error)
NewRandomKey() (Key, error)
NewSequenceKey(string) (Key, error)
NewRandomSequenceKey() (Key, error)
KeyLen() int
}
// CompositeKey provides a keyring mechanism: encrypt with first, decrypt with _any_
type CompositeKey []Key
// ErrorKey is a helper implementation that always returns an error
type ErrorKey struct {
Error error
}
/*
** KEY TYPES (factory)
*/
var (
factories = map[string]KeyFactory{}
factoriesMut sync.Mutex
)
// RegisterCipher registers a custom cipher. Useful for backwards compatibility or very specific needs,
// otherwise the provided implementations are recommended.
func RegisterCipher(name string, f KeyFactory) {
if f == nil {
return
}
factoriesMut.Lock()
defer factoriesMut.Unlock()
_, ok := factories[name]
if ok {
panic(fmt.Sprintf("Danger! Conflicting encryption key factories: %s", name))
}
factories[name] = f
}
// NewKey instantiates a new key with a given cipher.
func NewKey(cipher string, key string) (Key, error) {
f, err := GetKeyFactory(cipher)
if err != nil {
return nil, fmt.Errorf("unable to get key factory from cipher '%s': %w", cipher, err)
}
return f.NewKey(key)
}
// NewRandomKey instantiates a new random key with a given cipher.
func NewRandomKey(cipher string) (Key, error) {
f, err := GetKeyFactory(cipher)
if err != nil {
return nil, err
}
return f.NewRandomKey()
}
// GetKeyFactory retrieves the factory function from a cipher name
func GetKeyFactory(name string) (KeyFactory, error) {
if name == "" {
return nil, errors.New("trying to instantiate an encryption key without specifying a cipher")
}
factoriesMut.Lock()
defer factoriesMut.Unlock()
f, ok := factories[name]
if !ok {
return nil, fmt.Errorf("unknown cipher '%s'", name)
}
return f, nil
}
/*
** COMPOSITE ENCRYPTION KEY: keyring mechanism, always encrypt with first key, decrypt with _any_
*/
// Encrypt arbitrary data with the first key (highest priority)
func (c CompositeKey) Encrypt(text []byte, extra ...[]byte) ([]byte, error) {
if len(c) == 0 {
return nil, errors.New("empty composite encryption key")
}
return c[0].Encrypt(text, extra...)
}
// DecryptUncap decrypts abitrary data with _any_ key, and returns the key that was used.
// Useful for batch processes which may want to repeat decrypt operations using the same key without
// going through the key ring logic each time.
func (c CompositeKey) DecryptUncap(text []byte, extra ...[]byte) (Key, []byte, error) {
for _, k := range c {
b, err := k.Decrypt(text, extra...)
if err == nil {
return k, b, nil
}
}
return nil, nil, errors.New("failed to decrypt with all keys")
}
// Decrypt arbitrary data with _any_ key
func (c CompositeKey) Decrypt(text []byte, extra ...[]byte) ([]byte, error) {
for _, k := range c {
b, err := k.Decrypt(text, extra...)
if err == nil {
return b, nil
}
}
return nil, errors.New("failed to decrypt with all keys")
}
// EncryptMarshal encrypts an object with the first key (highest priority)
func (c CompositeKey) EncryptMarshal(i interface{}, extra ...[]byte) (string, error) {
if len(c) == 0 {
return "", errors.New("empty composite encryption key")
}
return c[0].EncryptMarshal(i, extra...)
}
// DecryptMarshal decrypts an object with _any_ key
func (c CompositeKey) DecryptMarshal(s string, target interface{}, extra ...[]byte) error {
var firstErr error
for _, k := range c {
err := k.DecryptMarshal(s, target, extra...)
if err == nil {
return nil
}
if firstErr == nil {
firstErr = err
}
}
// none worked, return the first error for cleaner propagation
if firstErr != nil {
return firstErr
}
return errors.New("failed to decrypt marshal with all keys")
}
// Wait for _all_ the keys to be ready
func (c CompositeKey) Wait() {
for _, k := range c {
k.Wait()
}
}
// String is not implemented for composite keys
func (c CompositeKey) String() (string, error) {
return "", errors.New("String operation unsupported for composite key")
}
/*
** ERROR KEY: respects Key interface, always returns error (helper)
*/
// Encrypt returns the predefined error
func (e ErrorKey) Encrypt(t []byte, extra ...[]byte) ([]byte, error) {
return nil, e.Error
}
// Decrypt returns the predefined error
func (e ErrorKey) Decrypt(t []byte, extra ...[]byte) ([]byte, error) {
return nil, e.Error
}
// EncryptMarshal returns the predefined error
func (e ErrorKey) EncryptMarshal(i interface{}, extra ...[]byte) (string, error) {
return "", e.Error
}
// DecryptMarshal returns the predefined error
func (e ErrorKey) DecryptMarshal(s string, i interface{}, extra ...[]byte) error {
return e.Error
}
// Wait is a no-op
func (e ErrorKey) Wait() {
}
// String returns the predefined error
func (e ErrorKey) String() (string, error) {
return "", e.Error
}
type writer struct {
k Key
w io.Writer
currentSecret bytes.Buffer
extras [][]byte
}
// NewWriter instantiates an io.WriteCloser to help you encrypt while you write in a standard io.Writer.
// Internally it stores in an internal buffer the content you want to encrypt. This internal is flushed, encrypted
// and write to the targeted io.Writer on Close().
func NewWriter(w io.Writer, k Key, extra ...[]byte) io.WriteCloser {
return &writer{
k: k,
w: w,
extras: extra,
}
}
// Close closes the writer. First it reads the internal buffer, then it encrypts its content.
// Finally the encrypted content is written to the destination writer.
// The error returned must be checked, you should not call Close on defer.
// If the destication writer is also a io.Write
func (sw *writer) Close() error {
// Encrypt the internal buffer
encData, err := sw.k.Encrypt(sw.currentSecret.Bytes(), sw.extras...)
if err != nil {
return err
}
// Write the encrypted bytes to the destination writer
n, err := sw.w.Write(encData)
switch {
case n != len(encData):
return errors.New("something went wrong during write to internal buffer")
case err != nil:
return err
}
// If the destication writer is a io.Closer: close it
c, ok := sw.w.(io.Closer)
if ok {
return c.Close()
}
return nil
}
// Write appends the contents of b to the internal buffer.
// The returned value n is the length of p; err is always nil. If the
// buffer becomes too large, Write will panic with ErrTooLarge.
func (sw *writer) Write(b []byte) (int, error) {
// copy clear data in the internal buffer
// it will be encrypted on close
return sw.currentSecret.Write(b)
}
// NewReader returns a new Reader which is able to decrypt the source io.Reader.
// It returns an error if the source io.Reader is unreadable with the provided Key and extras.
func NewReader(r io.Reader, k Key, extra ...[]byte) (io.Reader, error) {
// Read all the encrypted data in the internal buffer
var buffer bytes.Buffer
_, err := io.Copy(&buffer, r)
if err != nil {
return nil, err
}
// Decrypt all the buffer
btes := buffer.Bytes()
decData, err := k.Decrypt(btes, extra...)
if err != nil {
return nil, err
}
return bytes.NewReader(decData), nil
}