crypto: move reader and writer to its own file
This commit is contained in:
parent
64f7d4d611
commit
7e6acfe44d
3 changed files with 207 additions and 195 deletions
195
crypto/crypto.go
195
crypto/crypto.go
|
@ -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
97
crypto/reader.go
Normal 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
110
crypto/writer.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue