forked from TrueCloudLab/restic
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
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/restic/restic/chunker"
|
"github.com/restic/restic/chunker"
|
||||||
"golang.org/x/crypto/poly1305"
|
"golang.org/x/crypto/poly1305"
|
||||||
|
@ -300,194 +296,3 @@ func KDF(N, R, P int, salt []byte, password string) (*Key, error) {
|
||||||
|
|
||||||
return derKeys, nil
|
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