added fs encryption overlay
This commit is contained in:
parent
2039086696
commit
fd1e8118a4
28 changed files with 3994 additions and 30 deletions
155
pkg/provider/crypto/writer.go
Normal file
155
pkg/provider/crypto/writer.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"cachefs/pkg/chunk"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
type cryptoWriter interface {
|
||||
io.Writer
|
||||
io.WriterAt
|
||||
}
|
||||
|
||||
const lastChunkFlag = 0x01
|
||||
|
||||
func setLastChunkFlag(nonce *[NonceSize]byte) {
|
||||
nonce[len(nonce)-1] = lastChunkFlag
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
a cipher.AEAD
|
||||
dst cryptoWriter
|
||||
unwritten []byte // backed by buf
|
||||
buf [FullSize]byte
|
||||
nonce [NonceSize]byte
|
||||
err error
|
||||
cm map[int64]*chunkWriter
|
||||
last *chunkWriter
|
||||
flush bool
|
||||
}
|
||||
|
||||
func newWriter(key []byte, dst cryptoWriter) (*writer, error) {
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := &writer{
|
||||
a: aead,
|
||||
dst: dst,
|
||||
cm: make(map[int64]*chunkWriter),
|
||||
}
|
||||
w.unwritten = w.buf[:0]
|
||||
return w, nil
|
||||
}
|
||||
|
||||
type chunkWriter struct {
|
||||
offset int64
|
||||
cn int64
|
||||
w *writer
|
||||
buf [ChunkSize]byte
|
||||
nonce [NonceSize]byte
|
||||
chunks chunk.Chunks
|
||||
}
|
||||
|
||||
func (c *chunkWriter) writeAt(p []byte, offset int64) (n int, err error) {
|
||||
n = copy(c.buf[offset:], p)
|
||||
c.chunks.Add(offset, n)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *chunkWriter) full() bool {
|
||||
if len(c.chunks) == 1 {
|
||||
return c.chunks[0][1] == ChunkSize
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *chunkWriter) flush(last bool) (n int, err error) {
|
||||
wb := c.buf[:0]
|
||||
if last {
|
||||
setLastChunkFlag(&c.nonce)
|
||||
s := c.chunks.Size()
|
||||
wb = c.buf[:s]
|
||||
n = int(s)
|
||||
} else {
|
||||
wb = c.buf[:]
|
||||
n = len(c.buf)
|
||||
}
|
||||
buf := c.w.a.Seal(wb[:0], c.nonce[:], wb, nil)
|
||||
_, err = c.w.dst.WriteAt(buf, c.offset)
|
||||
return
|
||||
}
|
||||
|
||||
func (w *writer) Write(p []byte) (n int, err error) {
|
||||
// TODO: consider refactoring with a bytes.Buffer.
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
total := len(p)
|
||||
for len(p) > 0 {
|
||||
freeBuf := w.buf[len(w.unwritten):ChunkSize]
|
||||
n := copy(freeBuf, p)
|
||||
p = p[n:]
|
||||
w.unwritten = w.unwritten[:len(w.unwritten)+n]
|
||||
w.flush = true
|
||||
|
||||
if len(w.unwritten) == ChunkSize && len(p) > 0 {
|
||||
if err := w.flushChunk(notLastChunk); err != nil {
|
||||
w.err = err
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (w *writer) Close() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
|
||||
if w.last != nil {
|
||||
_, w.err = w.last.flush(lastChunk)
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
}
|
||||
|
||||
if w.flush {
|
||||
w.err = w.flushChunk(lastChunk)
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
}
|
||||
|
||||
w.err = errors.New("stream.Writer is already closed")
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
lastChunk = true
|
||||
notLastChunk = false
|
||||
)
|
||||
|
||||
func (w *writer) flushChunk(last bool) error {
|
||||
if !last && len(w.unwritten) != ChunkSize {
|
||||
panic("stream: internal error: flush called with partial chunk")
|
||||
}
|
||||
|
||||
if last {
|
||||
setLastChunkFlag(&w.nonce)
|
||||
}
|
||||
buf := w.a.Seal(w.buf[:0], w.nonce[:], w.unwritten, nil)
|
||||
_, err := w.dst.Write(buf)
|
||||
w.unwritten = w.buf[:0]
|
||||
incNonce(&w.nonce)
|
||||
return err
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue