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
|
package s3crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"io"
)
// AESCBC is a symmetric crypto algorithm. This algorithm
// requires a padder due to CBC needing to be of the same block
// size. AES CBC is vulnerable to Padding Oracle attacks and
// so should be avoided when possible.
type aesCBC struct {
encrypter cipher.BlockMode
decrypter cipher.BlockMode
padder Padder
}
// newAESCBC creates a new AES CBC cipher. Expects keys to be of
// the correct size.
func newAESCBC(cd CipherData, padder Padder) (Cipher, error) {
block, err := aes.NewCipher(cd.Key)
if err != nil {
return nil, err
}
encrypter := cipher.NewCBCEncrypter(block, cd.IV)
decrypter := cipher.NewCBCDecrypter(block, cd.IV)
return &aesCBC{encrypter, decrypter, padder}, nil
}
// Encrypt will encrypt the data using AES CBC by returning
// an io.Reader. The io.Reader will encrypt the data as Read
// is called.
func (c *aesCBC) Encrypt(src io.Reader) io.Reader {
reader := &cbcEncryptReader{
encrypter: c.encrypter,
src: src,
padder: c.padder,
}
return reader
}
type cbcEncryptReader struct {
encrypter cipher.BlockMode
src io.Reader
padder Padder
size int
buf bytes.Buffer
}
// Read will read from our io.Reader and encrypt the data as necessary.
// Due to padding, we have to do some logic that when we encounter an
// end of file to pad properly.
func (reader *cbcEncryptReader) Read(data []byte) (int, error) {
n, err := reader.src.Read(data)
reader.size += n
blockSize := reader.encrypter.BlockSize()
reader.buf.Write(data[:n])
if err == io.EOF {
b := make([]byte, getSliceSize(blockSize, reader.buf.Len(), len(data)))
n, err = reader.buf.Read(b)
if err != nil && err != io.EOF {
return n, err
}
// The buffer is now empty, we can now pad the data
if reader.buf.Len() == 0 {
b, err = reader.padder.Pad(b[:n], reader.size)
if err != nil {
return n, err
}
n = len(b)
err = io.EOF
}
// We only want to encrypt if we have read anything
if n > 0 {
reader.encrypter.CryptBlocks(data, b)
}
return n, err
}
if err != nil {
return n, err
}
if size := reader.buf.Len(); size >= blockSize {
nBlocks := size / blockSize
if size > len(data) {
nBlocks = len(data) / blockSize
}
if nBlocks > 0 {
b := make([]byte, nBlocks*blockSize)
n, _ = reader.buf.Read(b)
reader.encrypter.CryptBlocks(data, b[:n])
}
} else {
n = 0
}
return n, nil
}
// Decrypt will decrypt the data using AES CBC
func (c *aesCBC) Decrypt(src io.Reader) io.Reader {
return &cbcDecryptReader{
decrypter: c.decrypter,
src: src,
padder: c.padder,
}
}
type cbcDecryptReader struct {
decrypter cipher.BlockMode
src io.Reader
padder Padder
buf bytes.Buffer
}
// Read will read from our io.Reader and decrypt the data as necessary.
// Due to padding, we have to do some logic that when we encounter an
// end of file to pad properly.
func (reader *cbcDecryptReader) Read(data []byte) (int, error) {
n, err := reader.src.Read(data)
blockSize := reader.decrypter.BlockSize()
reader.buf.Write(data[:n])
if err == io.EOF {
b := make([]byte, getSliceSize(blockSize, reader.buf.Len(), len(data)))
n, err = reader.buf.Read(b)
if err != nil && err != io.EOF {
return n, err
}
// We only want to decrypt if we have read anything
if n > 0 {
reader.decrypter.CryptBlocks(data, b)
}
if reader.buf.Len() == 0 {
b, err = reader.padder.Unpad(data[:n])
n = len(b)
if err != nil {
return n, err
}
err = io.EOF
}
return n, err
}
if err != nil {
return n, err
}
if size := reader.buf.Len(); size >= blockSize {
nBlocks := size / blockSize
if size > len(data) {
nBlocks = len(data) / blockSize
}
// The last block is always padded. This will allow us to unpad
// when we receive an io.EOF error
nBlocks -= blockSize
if nBlocks > 0 {
b := make([]byte, nBlocks*blockSize)
n, _ = reader.buf.Read(b)
reader.decrypter.CryptBlocks(data, b[:n])
} else {
n = 0
}
}
return n, nil
}
// getSliceSize will return the correct amount of bytes we need to
// read with regards to padding.
func getSliceSize(blockSize, bufSize, dataSize int) int {
size := bufSize
if bufSize > dataSize {
size = dataSize
}
size = size - (size % blockSize) - blockSize
if size <= 0 {
size = blockSize
}
return size
}
|