crypto: move reader and writer to its own file

This commit is contained in:
Alexander Neumann 2015-04-12 10:57:41 +02:00
parent 64f7d4d611
commit 7e6acfe44d
3 changed files with 207 additions and 195 deletions

View File

@ -1,16 +1,12 @@
package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"sync"
"github.com/restic/restic/chunker"
"golang.org/x/crypto/poly1305"
@ -300,194 +296,3 @@ func KDF(N, R, P int, salt []byte, password string) (*Key, error) {
return derKeys, nil
}
type encryptWriter struct {
iv iv
wroteIV bool
data *bytes.Buffer
key *Key
s cipher.Stream
w io.Writer
origWr io.Writer
err error // remember error writing iv
}
func (e *encryptWriter) Close() error {
// write mac
mac := poly1305_sign(e.data.Bytes()[ivSize:], e.data.Bytes()[:ivSize], &e.key.Sign)
_, err := e.origWr.Write(mac)
if err != nil {
return err
}
// return buffer
bufPool.Put(e.data.Bytes())
return nil
}
const encryptWriterChunkSize = 512 * 1024 // 512 KiB
var encryptWriterBufPool = sync.Pool{
New: func() interface{} {
return make([]byte, encryptWriterChunkSize)
},
}
func (e *encryptWriter) Write(p []byte) (int, error) {
// write iv first
if !e.wroteIV {
_, e.err = e.origWr.Write(e.iv[:])
e.wroteIV = true
}
if e.err != nil {
return 0, e.err
}
buf := encryptWriterBufPool.Get().([]byte)
defer encryptWriterBufPool.Put(buf)
written := 0
for len(p) > 0 {
max := len(p)
if max > encryptWriterChunkSize {
max = encryptWriterChunkSize
}
e.s.XORKeyStream(buf, p[:max])
n, err := e.w.Write(buf[:max])
if n != max {
if err == nil { // should never happen
err = io.ErrShortWrite
}
}
written += n
p = p[n:]
if err != nil {
e.err = err
return written, err
}
}
return written, nil
}
// EncryptTo buffers data written to the returned io.WriteCloser. When Close()
// is called, the data is encrypted an written to the underlying writer.
func EncryptTo(ks *Key, wr io.Writer) io.WriteCloser {
ew := &encryptWriter{
iv: newIV(),
data: bytes.NewBuffer(getBuffer()[:0]),
key: ks,
origWr: wr,
}
// buffer iv for mac
_, err := ew.data.Write(ew.iv[:])
if err != nil {
panic(err)
}
c, err := aes.NewCipher(ks.Encrypt[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
ew.s = cipher.NewCTR(c, ew.iv[:])
ew.w = io.MultiWriter(ew.data, wr)
return ew
}
type decryptReader struct {
buf []byte
pos int
}
func (d *decryptReader) Read(dst []byte) (int, error) {
if d.buf == nil {
return 0, io.EOF
}
if len(dst) == 0 {
return 0, nil
}
remaining := len(d.buf) - d.pos
if len(dst) >= remaining {
n := copy(dst, d.buf[d.pos:])
d.Close()
return n, io.EOF
}
n := copy(dst, d.buf[d.pos:d.pos+len(dst)])
d.pos += n
return n, nil
}
func (d *decryptReader) ReadByte() (c byte, err error) {
if d.buf == nil {
return 0, io.EOF
}
remaining := len(d.buf) - d.pos
if remaining == 1 {
c = d.buf[d.pos]
d.Close()
return c, io.EOF
}
c = d.buf[d.pos]
d.pos++
return
}
func (d *decryptReader) Close() error {
if d.buf == nil {
return nil
}
freeBuffer(d.buf)
d.buf = nil
return nil
}
// DecryptFrom verifies and decrypts the ciphertext read from rd with ks and
// makes it available on the returned Reader. Ciphertext must be in the form IV
// || Ciphertext || MAC. In order to correctly verify the ciphertext, rd is
// drained, locally buffered and made available on the returned Reader
// afterwards. If a MAC verification failure is observed, it is returned
// immediately.
func DecryptFrom(ks *Key, rd io.Reader) (io.ReadCloser, error) {
ciphertext := getBuffer()
ciphertext = ciphertext[0:cap(ciphertext)]
n, err := io.ReadFull(rd, ciphertext)
if err != io.ErrUnexpectedEOF {
// read remaining data
buf, e := ioutil.ReadAll(rd)
ciphertext = append(ciphertext, buf...)
n += len(buf)
err = e
} else {
err = nil
}
if err != nil {
return nil, err
}
ciphertext = ciphertext[:n]
// decrypt
ciphertext, err = Decrypt(ks, ciphertext, ciphertext)
if err != nil {
return nil, err
}
return &decryptReader{buf: ciphertext}, nil
}

97
crypto/reader.go Normal file
View File

@ -0,0 +1,97 @@
package crypto
import (
"io"
"io/ioutil"
)
type decryptReader struct {
buf []byte
pos int
}
func (d *decryptReader) Read(dst []byte) (int, error) {
if d.buf == nil {
return 0, io.EOF
}
if len(dst) == 0 {
return 0, nil
}
remaining := len(d.buf) - d.pos
if len(dst) >= remaining {
n := copy(dst, d.buf[d.pos:])
d.Close()
return n, io.EOF
}
n := copy(dst, d.buf[d.pos:d.pos+len(dst)])
d.pos += n
return n, nil
}
func (d *decryptReader) ReadByte() (c byte, err error) {
if d.buf == nil {
return 0, io.EOF
}
remaining := len(d.buf) - d.pos
if remaining == 1 {
c = d.buf[d.pos]
d.Close()
return c, io.EOF
}
c = d.buf[d.pos]
d.pos++
return
}
func (d *decryptReader) Close() error {
if d.buf == nil {
return nil
}
freeBuffer(d.buf)
d.buf = nil
return nil
}
// DecryptFrom verifies and decrypts the ciphertext read from rd with ks and
// makes it available on the returned Reader. Ciphertext must be in the form IV
// || Ciphertext || MAC. In order to correctly verify the ciphertext, rd is
// drained, locally buffered and made available on the returned Reader
// afterwards. If a MAC verification failure is observed, it is returned
// immediately.
func DecryptFrom(ks *Key, rd io.Reader) (io.ReadCloser, error) {
ciphertext := getBuffer()
ciphertext = ciphertext[0:cap(ciphertext)]
n, err := io.ReadFull(rd, ciphertext)
if err != io.ErrUnexpectedEOF {
// read remaining data
buf, e := ioutil.ReadAll(rd)
ciphertext = append(ciphertext, buf...)
n += len(buf)
err = e
} else {
err = nil
}
if err != nil {
return nil, err
}
ciphertext = ciphertext[:n]
// decrypt
ciphertext, err = Decrypt(ks, ciphertext, ciphertext)
if err != nil {
return nil, err
}
return &decryptReader{buf: ciphertext}, nil
}

110
crypto/writer.go Normal file
View File

@ -0,0 +1,110 @@
package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"
"io"
"sync"
)
type encryptWriter struct {
iv iv
wroteIV bool
data *bytes.Buffer
key *Key
s cipher.Stream
w io.Writer
origWr io.Writer
err error // remember error writing iv
}
func (e *encryptWriter) Close() error {
// write mac
mac := poly1305_sign(e.data.Bytes()[ivSize:], e.data.Bytes()[:ivSize], &e.key.Sign)
_, err := e.origWr.Write(mac)
if err != nil {
return err
}
// return buffer
bufPool.Put(e.data.Bytes())
return nil
}
const encryptWriterChunkSize = 512 * 1024 // 512 KiB
var encryptWriterBufPool = sync.Pool{
New: func() interface{} {
return make([]byte, encryptWriterChunkSize)
},
}
func (e *encryptWriter) Write(p []byte) (int, error) {
// write iv first
if !e.wroteIV {
_, e.err = e.origWr.Write(e.iv[:])
e.wroteIV = true
}
if e.err != nil {
return 0, e.err
}
buf := encryptWriterBufPool.Get().([]byte)
defer encryptWriterBufPool.Put(buf)
written := 0
for len(p) > 0 {
max := len(p)
if max > encryptWriterChunkSize {
max = encryptWriterChunkSize
}
e.s.XORKeyStream(buf, p[:max])
n, err := e.w.Write(buf[:max])
if n != max {
if err == nil { // should never happen
err = io.ErrShortWrite
}
}
written += n
p = p[n:]
if err != nil {
e.err = err
return written, err
}
}
return written, nil
}
// EncryptTo buffers data written to the returned io.WriteCloser. When Close()
// is called, the data is encrypted an written to the underlying writer.
func EncryptTo(ks *Key, wr io.Writer) io.WriteCloser {
ew := &encryptWriter{
iv: newIV(),
data: bytes.NewBuffer(getBuffer()[:0]),
key: ks,
origWr: wr,
}
// buffer iv for mac
_, err := ew.data.Write(ew.iv[:])
if err != nil {
panic(err)
}
c, err := aes.NewCipher(ks.Encrypt[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
ew.s = cipher.NewCTR(c, ew.iv[:])
ew.w = io.MultiWriter(ew.data, wr)
return ew
}