-
Notifications
You must be signed in to change notification settings - Fork 0
/
aes_reader.go
executable file
·103 lines (77 loc) · 2.09 KB
/
aes_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
package gocrypt
import (
"bytes"
"crypto/cipher"
"errors"
"io"
)
var ErrPaddingError error = errors.New("padding error")
type AESReader struct {
upstream io.Reader
BlockSize int
IV []byte
key []byte
buffer bytes.Buffer
eof bool
cipherType int
block cipher.Block
blockMode cipher.BlockMode
stream cipher.Stream
aead cipher.AEAD
chunkSize int
}
/* Read() reads from upstream ciphertext, returning plaintext.
* Does internal buffering, and unpadding
*/
func (r *AESReader) Read(dst []byte) (int, error) {
//If upstream is already EOF'ed, read directly from buffer
if r.eof {
return r.buffer.Read(dst)
}
switch r.cipherType {
case blockCipherType:
//Read to the nearest multiple of block size + 1 block
toRead := len(dst)
cipherText := make([]byte, nearestMultiple(toRead, AESBlockSize))
read, err := r.upstream.Read(cipherText)
if read != len(cipherText) || err == io.EOF {
r.eof = true
} else if err != nil {
return 0, err
}
if read%AESBlockSize != 0 {
return 0, ErrPaddingError
}
plainText := make([]byte, len(cipherText))
r.blockMode.CryptBlocks(plainText[:read], cipherText[:read])
if r.eof {
//Check if this read resulted in not an entire blocks worth of bytes, if so the padding must be at the end of the current buffer and should be removed
if read == 0 {
rawBytes := r.buffer.Bytes()
r.buffer.Truncate(len(rawBytes) - int(rawBytes[len(rawBytes)-1]))
return r.buffer.Read(dst)
}
plainText = plainText[:read-int(plainText[read-1])]
}
_, err = r.buffer.Write(plainText)
if err != nil {
return 0, err
}
case streamCipherType:
cipherText := make([]byte, len(dst))
read, err := r.upstream.Read(cipherText)
if read != len(cipherText) || err == io.EOF {
r.eof = true
} else if err != nil {
return read, err
}
r.stream.XORKeyStream(dst[:read], cipherText[:read])
return read, nil
default:
return 0, ErrUnknownCipherType
}
return r.buffer.Read(dst)
}
func nearestMultiple(wanted int, multiple int) int {
return (wanted + (multiple-wanted%multiple)%multiple) + wanted
}