-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathdecrypt_reader.go
More file actions
165 lines (150 loc) · 3.58 KB
/
Copy pathdecrypt_reader.go
File metadata and controls
165 lines (150 loc) · 3.58 KB
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
package rardecode
import (
"crypto/aes"
"crypto/cipher"
"io"
)
// cipherBlockReader implements Block Mode decryption of an io.Reader object.
type cipherBlockReader struct {
br byteReader
mode cipher.BlockMode
inbuf []byte // raw input blocks not yet decrypted
outbuf []byte // output buffer used when output slice < block size
block []byte // input/output buffer for a single block
}
func (cr *cipherBlockReader) fillOutbuf() error {
l := len(cr.inbuf)
_, err := io.ReadFull(cr.br, cr.block[l:])
if err != nil {
return err
}
cr.mode.CryptBlocks(cr.block, cr.block)
cr.outbuf = cr.block
return nil
}
func (cr *cipherBlockReader) ReadByte() (byte, error) {
if len(cr.outbuf) == 0 {
err := cr.fillOutbuf()
if err != nil {
return 0, err
}
}
b := cr.outbuf[0]
cr.outbuf = cr.outbuf[1:]
return b, nil
}
// Read reads and decrypts data into p.
// If the input is not a multiple of the cipher block size,
// the trailing bytes will be ignored.
func (cr *cipherBlockReader) Read(p []byte) (int, error) {
var n int
if len(cr.outbuf) > 0 {
n = copy(p, cr.outbuf)
cr.outbuf = cr.outbuf[n:]
return n, nil
}
blockSize := cr.mode.BlockSize()
if len(p) < blockSize {
// use cr.block as buffer
err := cr.fillOutbuf()
if err != nil {
return 0, err
}
n = copy(p, cr.outbuf)
cr.outbuf = cr.outbuf[n:]
return n, nil
}
// use p as buffer (but round down to multiple of block size)
p = p[:len(p)-(len(p)%blockSize)]
l := len(cr.inbuf)
if l > 0 {
copy(p, cr.inbuf)
cr.inbuf = nil
}
n, err := io.ReadAtLeast(cr.br, p[l:], blockSize-l)
if err != nil {
return 0, err
}
n += l
p = p[:n]
n -= n % blockSize
if n != len(p) {
l = copy(cr.block, p[n:])
cr.inbuf = cr.block[:l]
p = p[:n]
}
cr.mode.CryptBlocks(p, p)
return n, nil
}
func (cr *cipherBlockReader) writeToN(w io.Writer, n int64) (int64, error) {
if n == 0 {
return 0, nil
}
var tot int64
var err error
for tot != n && err == nil {
if len(cr.outbuf) == 0 {
err = cr.fillOutbuf()
if err != nil {
break
}
}
buf := cr.outbuf
if n > 0 {
todo := n - tot
if todo < int64(len(buf)) {
buf = buf[:todo]
}
}
var l int
l, err = w.Write(buf)
tot += int64(l)
cr.outbuf = cr.outbuf[l:]
}
if n < 0 && err == io.EOF {
err = nil
}
return tot, err
}
func (cr *cipherBlockReader) WriteTo(w io.Writer) (int64, error) {
return cr.writeToN(w, -1)
}
func newCipherBlockReader(r byteReader, mode cipher.BlockMode) *cipherBlockReader {
return &cipherBlockReader{
br: r,
mode: mode,
block: make([]byte, mode.BlockSize()),
}
}
// newAesDecryptReader returns a cipherBlockReader that decrypts input from a given io.Reader using AES.
func newAesDecryptReader(r byteReader, key, iv []byte) (*cipherBlockReader, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
mode := cipher.NewCBCDecrypter(block, iv)
return newCipherBlockReader(r, mode), nil
}
type cipherBlockFileReader struct {
archiveFile
cbr *cipherBlockReader
}
func (cr *cipherBlockFileReader) ReadByte() (byte, error) {
return cr.cbr.ReadByte()
}
func (cr *cipherBlockFileReader) Read(p []byte) (int, error) {
return cr.cbr.Read(p)
}
func (cr *cipherBlockFileReader) WriteTo(w io.Writer) (int64, error) {
return cr.cbr.WriteTo(w)
}
func (cr *cipherBlockFileReader) writeToN(w io.Writer, n int64) (int64, error) {
return cr.cbr.writeToN(w, n)
}
func newAesDecryptFileReader(r archiveFile, key, iv []byte) (*cipherBlockFileReader, error) {
cbr, err := newAesDecryptReader(r, key, iv)
if err != nil {
return nil, err
}
return &cipherBlockFileReader{archiveFile: r, cbr: cbr}, nil
}