/
reader.go
352 lines (283 loc) · 8.48 KB
/
reader.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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
package encrypt
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
// Reader decrypts and encrypted datastream from Reader.
type Reader struct {
// Underlying reader
io.Reader
aeadCommon
// Caches leftovers from unread blocks
backlog *bytes.Reader
// Last index of the byte the user visited.
// (Used to avoid re-reads in Seek())
// This does *not* equal the seek offset of the underlying stream.
lastDecSeekPos int64
// lastEncSeekPos saves the current position of the underlying stream.
// it is used mostly for ensuring SEEK_END works.
lastEncSeekPos int64
// Parsed header info
info *HeaderInfo
// true once readHeader() was called
parsedHeader bool
// Buffer for decrypted data (MaxBlockSize big)
decBuf []byte
// true as long readBlock was not successful
isInitialRead bool
// Total size of the underlying stream in bytes.
// This is only set when SEEK_END was used.
endOffsetEnc int64
}
func (r *Reader) readHeaderIfNotDone() error {
if r.parsedHeader {
return nil
}
r.parsedHeader = true
header := make([]byte, headerSize)
n, err := r.Reader.Read(header)
if err != nil {
return err
}
if n != headerSize {
return fmt.Errorf("No valid header found, damaged file?")
}
info, err := ParseHeader(header, r.key)
if err != nil {
return err
}
if info.Version != 1 {
return fmt.Errorf("This implementation does not support versions != 1")
}
if uint32(len(r.key)) != info.Keylen {
return fmt.Errorf("Key length differs: file=%d, user=%d", info.Keylen, len(r.key))
}
r.info = info
if err := r.initAeadCommon(r.key, info.Cipher, int64(r.info.Blocklen)); err != nil {
return err
}
r.lastEncSeekPos += headerSize
r.decBuf = make([]byte, 0, r.info.Blocklen)
return nil
}
// Read from source and decrypt.
//
// This method always decrypts one block to optimize for continuous reads. If
// dest is too small to hold the block, the decrypted text is cached for the
// next read.
func (r *Reader) Read(dest []byte) (int, error) {
// Make sure we have the info needed to parse the header:
if err := r.readHeaderIfNotDone(); err != nil {
return 0, err
}
readBytes := 0
// Try our best to fill len(dest)
for readBytes < len(dest) {
if r.backlog.Len() == 0 {
if _, rerr := r.readBlock(); rerr != nil && rerr != io.EOF {
return readBytes, rerr
}
}
n, berr := r.backlog.Read(dest[readBytes:])
r.lastDecSeekPos += int64(n)
readBytes += n
if berr == io.EOF {
return readBytes, io.EOF
}
}
return readBytes, nil
}
// Fill internal buffer with current block
func (r *Reader) readBlock() (int, error) {
if r.info == nil {
return 0, fmt.Errorf("Invalid header data")
}
// Read nonce:
if n, err := r.Reader.Read(r.nonce); err != nil {
return 0, err
} else if n != r.aead.NonceSize() {
return 0, fmt.Errorf("nonce size mismatch; should: %d - have: %d",
r.aead.NonceSize(), n)
}
// Convert to block number:
readBlockNum := binary.LittleEndian.Uint64(r.nonce)
// Check the block number:
currBlockNum := uint64(r.lastDecSeekPos / int64(r.info.Blocklen))
if currBlockNum != readBlockNum {
return 0, fmt.Errorf(
"bad block number; as %d, should be %d", readBlockNum, currBlockNum,
)
}
// Read the *whole* block from the raw stream
N := int(r.info.Blocklen) + r.aead.Overhead()
n, err := io.ReadAtLeast(r.Reader, r.encBuf[:N], N)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
return 0, err
}
r.lastEncSeekPos += int64(n) + int64(len(r.nonce))
r.decBuf, err = r.aead.Open(r.decBuf[:0], r.nonce, r.encBuf[:n], nil)
if err != nil {
return 0, err
}
r.backlog = bytes.NewReader(r.decBuf)
r.isInitialRead = false
return len(r.decBuf), nil
}
// Seek into the encrypted stream.
//
// Note that the seek offset is relative to the decrypted data,
// not to the underlying, encrypted stream.
//
// Mixing SEEK_CUR and SEEK_SET might not a good idea,
// since a seek might involve reading a whole encrypted block.
// Therefore relative seek offset
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
// Check if seeking is supported:
seeker, ok := r.Reader.(io.Seeker)
if !ok {
return 0, fmt.Errorf("Seek is not supported by underlying datastream")
}
if err := r.readHeaderIfNotDone(); err != nil {
return 0, err
}
// set to true when an actual call to seeker.Seek() was made.
wasMoved := false
// Constants and assumption on the stream below:
blockHeaderSize := int64(r.aead.NonceSize())
blockOverhead := blockHeaderSize + int64(r.aead.Overhead())
totalBlockSize := blockOverhead + int64(r.info.Blocklen)
// absolute Offset in the decrypted stream
absOffsetDec := int64(0)
// Convert possibly relative offset to absolute offset:
switch whence {
case io.SeekCurrent:
absOffsetDec = r.lastDecSeekPos + offset
case io.SeekStart:
absOffsetDec = offset
case io.SeekEnd:
// Try to figure out the end of the stream.
// This might be inefficient for some underlying readers,
// but is probably okay for ipfs.
//
// NOTE: We cache this not only for performance, but also
// as a workaround for a bug in ipfs.
// See: https://github.com/ipfs/go-ipfs/issues/2567
if r.endOffsetEnc < 0 {
endOffsetEnc, err := seeker.Seek(0, io.SeekEnd)
if err != nil && err != io.EOF {
return 0, err
}
r.endOffsetEnc = endOffsetEnc
}
// This computation is verbose on purporse,
// since the details might be confusing.
encLen := (r.endOffsetEnc - headerSize)
encRest := encLen % totalBlockSize
decBlocks := encLen / totalBlockSize
endOffsetDec := decBlocks * int64(r.info.Blocklen)
if encRest > 0 {
endOffsetDec += encRest - blockOverhead
}
absOffsetDec = endOffsetDec + offset
if absOffsetDec < 0 {
// That's the wrong end of file...
return 0, io.EOF
}
// For SEEK_END we need to make sure that we move the seek pointer
// back to a sensible position when we decide that no actual move
// is necessary further down this function.
defer func() {
if !wasMoved {
seeker.Seek(r.lastEncSeekPos, io.SeekStart)
}
}()
}
if absOffsetDec < 0 {
return 0, fmt.Errorf("Negative seek index: %d", absOffsetDec)
}
// Caller wanted to know only the current stream pos:
if absOffsetDec == r.lastDecSeekPos {
return absOffsetDec, nil
}
// Convert decrypted offset to encrypted offset
absOffsetEnc := headerSize + ((absOffsetDec / int64(r.info.Blocklen)) * totalBlockSize)
// Check if we're still in the same block as last time:
blockNum := absOffsetEnc / totalBlockSize
lastBlockNum := r.lastDecSeekPos / int64(r.info.Blocklen)
r.lastDecSeekPos = absOffsetDec
if lastBlockNum != blockNum || r.isInitialRead || whence == io.SeekEnd {
r.lastEncSeekPos = absOffsetEnc
// Seek to the beginning of the encrypted block:
wasMoved = true
if _, err := seeker.Seek(absOffsetEnc, io.SeekStart); err != nil {
return 0, err
}
// Make read consume the current block:
if _, err := r.readBlock(); err != nil {
return 0, err
}
}
// Reslice the backlog, so Read() does not return skipped data.
if _, err := r.backlog.Seek(absOffsetDec%int64(r.info.Blocklen), io.SeekStart); err != nil {
return 0, err
}
return absOffsetDec, nil
}
// WriteTo copies all data from `r` to `w`.
//
// It is intended to avoid unneeded copying by choosing a suitable buffer size
// and by directly reading block after block. io.Copy will use it automatically.
//
// It returns the number of written bytes and possible errors (but no io.EOF)
func (r *Reader) WriteTo(w io.Writer) (int64, error) {
// Make sure we have the info needed to parse the header:
if err := r.readHeaderIfNotDone(); err != nil {
return 0, err
}
n := int64(0)
// Backlog might be still filled if Read() or Seek() was done before:
if r.backlog.Len() > 0 {
bn, err := r.backlog.WriteTo(w)
if err != nil {
return bn, err
}
n += bn
r.lastDecSeekPos += bn
}
for {
nread, rerr := r.readBlock()
if rerr != nil && rerr != io.EOF {
return n, rerr
}
r.lastDecSeekPos += int64(nread)
nwrite, werr := w.Write(r.decBuf[:nread])
if werr != nil {
return n, werr
}
n += int64(nwrite)
if nwrite != nread {
return n, io.ErrShortWrite
}
if rerr == io.EOF {
break
}
}
return n, nil
}
// NewReader creates a new encrypted reader and validates the file header.
// The key is required to be KeySize bytes long.
func NewReader(r io.Reader, key []byte) (*Reader, error) {
reader := &Reader{
Reader: r,
backlog: bytes.NewReader([]byte{}),
parsedHeader: false,
isInitialRead: true,
endOffsetEnc: -1,
aeadCommon: aeadCommon{
key: key,
},
}
return reader, nil
}