-
Notifications
You must be signed in to change notification settings - Fork 0
/
cipher_reader.go
87 lines (81 loc) · 2.72 KB
/
cipher_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
package okapi
import (
"errors"
"io"
)
// CipherReader decrypts bytes read from the underlying Reader.
// CipherReader MUST be closed before it's discarded.
type CipherReader struct {
input io.Reader
buffer []byte
cipher Cipher
}
// NewCipherReader creates CipherReader wrapped around provided Reader.
// The associated cipher is created from the provided CipherSpec, key and iv.
// The optional buffer is used internally. If buffer is not provided,
// it will be created with DefaultBufferSize.
func NewCipherReader(in io.Reader, cs CipherSpec, key, iv, buffer []byte) *CipherReader {
if buffer == nil {
buffer = make([]byte, DefaultBufferSize)
}
return &CipherReader{input: in, cipher: cs.New(key, iv, false), buffer: buffer}
}
// Read reads necessary amount of input from the underlying Reader and decrypts it
// into the provided slice. It conforms to the io.Reader interface.
// Note that due to the nature of block ciphers, certain amount of read-ahead is necessary
// to provide the requested amount of bytes, although best effort is made to minimize the amount
// of read-ahead (generally only the input necessary to decrypt the last partially read block).
func (r *CipherReader) Read(out []byte) (int, error) {
buffered := r.cipher.BufferedSize()
written := 0
toWrite := len(out)
for toWrite >= len(r.buffer)+buffered {
read, err := r.bufferRead(r.buffer, out[written:])
if err != nil {
return written + read, err
}
written += read
toWrite -= read
}
if toWrite == 0 {
return written, nil
}
// last buffer fill
blockSize := r.cipher.BlockSize()
read := min(len(r.buffer), ((toWrite-1)/blockSize+1)*blockSize-buffered)
read, err := r.bufferRead(r.buffer[:read], out[written:])
if err != nil {
return written + read, err
}
written += read
toWrite -= read
if toWrite == 0 {
return written, nil
}
// we may still be few bytes short, read just enough to decrypt one more block
read = blockSize - r.cipher.BufferedSize()
read, err = r.bufferRead(r.buffer[:read], out[written:])
return written + read, err
}
func (r *CipherReader) bufferRead(buffer, out []byte) (int, error) {
read, err := r.input.Read(buffer)
if read == 0 {
return 0, err
}
// TODO: ignoring ins is fishy, what if the cipher does not consume all input?
_, outs := r.cipher.Update(buffer[:read], out)
return outs, err
}
// Close checks that there isn't any pending input left, then releases any associated resources, e.g. the cipher.
// If the underlying Reader is a Closer, then it Closes it as well.
func (r *CipherReader) Close() error {
defer r.cipher.Close()
outs := r.cipher.Finish(r.buffer)
if outs != 0 {
return errors.New("Unfinished cipher block")
}
if closer, ok := r.input.(io.Closer); ok {
return closer.Close()
}
return nil
}