-
Notifications
You must be signed in to change notification settings - Fork 0
/
writer.go
118 lines (92 loc) · 2.36 KB
/
writer.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
package libsodium
// #cgo CFLAGS: -I../../../tmp/libsodium/include
// #cgo LDFLAGS: -L../../../tmp/libsodium/lib -lsodium
// #include <sodium.h>
import "C"
import (
"io"
"sync"
"github.com/pkg/errors"
)
// Writer wraps ordinary writer with libsodium encryption
type Writer struct {
io.Writer
state C.crypto_secretstream_xchacha20poly1305_state
in []byte
out []byte
inIdx int
// In case of using io.Pipe we can't write header until reader doesn't read, therefor we use these sync
onceHeader sync.Once
key []byte
headerErr error
}
// NewWriter creates Writer from ordinary writer and key
func NewWriter(writer io.Writer, key []byte) io.WriteCloser {
return &Writer{
Writer: writer,
in: make([]byte, chunkSize),
out: make([]byte, chunkSize+C.crypto_secretstream_xchacha20poly1305_ABYTES),
key: key,
}
}
func (writer *Writer) writeHeader() {
header := make([]byte, C.crypto_secretstream_xchacha20poly1305_HEADERBYTES)
var state C.crypto_secretstream_xchacha20poly1305_state
C.crypto_secretstream_xchacha20poly1305_init_push(
&state,
(*C.uchar)(&header[0]),
(*C.uchar)(&writer.key[0]),
)
if _, err := writer.Writer.Write(header); err != nil {
writer.headerErr = errors.Wrap(err, "failed to write libsodium header")
return
}
writer.state = state
}
// Write implements io.Writer
func (writer *Writer) Write(p []byte) (n int, err error) {
writer.onceHeader.Do(writer.writeHeader)
if writer.headerErr != nil {
return 0, err
}
for n != len(p) {
count := copy(writer.in[writer.inIdx:], p[n:])
writer.inIdx += count
n += count
if writer.inIdx == len(writer.in) {
if err = writer.writeNextChunk(false); err != nil {
return
}
}
}
return
}
func (writer *Writer) writeNextChunk(last bool) (err error) {
var outLen C.ulonglong
var tag C.uchar
if last {
tag = C.crypto_secretstream_xchacha20poly1305_TAG_FINAL
}
C.crypto_secretstream_xchacha20poly1305_push(
&writer.state,
(*C.uchar)(&writer.out[0]),
&outLen,
(*C.uchar)(&writer.in[0]),
(C.ulonglong)(writer.inIdx),
(*C.uchar)(C.NULL),
(C.ulonglong)(0),
tag,
)
if _, err = writer.Writer.Write(writer.out[:int(outLen)]); err != nil {
return
}
writer.inIdx = 0
return
}
// Close implements io.Closer
func (writer *Writer) Close() (err error) {
if closer, ok := writer.Writer.(io.Closer); ok {
defer closer.Close()
}
return writer.writeNextChunk(true)
}