diff options
Diffstat (limited to 'internal/contentenc')
-rw-r--r-- | internal/contentenc/content.go | 27 | ||||
-rw-r--r-- | internal/contentenc/content_test.go | 4 | ||||
-rw-r--r-- | internal/contentenc/file_header.go | 30 | ||||
-rw-r--r-- | internal/contentenc/intrablock.go | 28 | ||||
-rw-r--r-- | internal/contentenc/offsets.go | 48 |
5 files changed, 75 insertions, 62 deletions
diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index ac2e8de..dd3b77a 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -11,21 +11,28 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) +// NonceMode determines how nonces are created. type NonceMode int const ( - // Default plaintext block size + // DefaultBS is the default plaintext block size DefaultBS = 4096 + // DefaultIVBits is the default length of IV, in bits. // We always use 128-bit IVs for file content, but the // key in the config file is encrypted with a 96-bit IV. DefaultIVBits = 128 - _ = iota // skip zero - RandomNonce NonceMode = iota + _ = iota // skip zero + // RandomNonce chooses a random nonce. + RandomNonce NonceMode = iota + // ReverseDeterministicNonce chooses a deterministic nonce, suitable for + // use in reverse mode. ReverseDeterministicNonce NonceMode = iota - ExternalNonce NonceMode = iota + // ExternalNonce derives a nonce from external sources. + ExternalNonce NonceMode = iota ) +// ContentEnc is used to encipher and decipher file content. type ContentEnc struct { // Cryptographic primitives cryptoCore *cryptocore.CryptoCore @@ -39,8 +46,8 @@ type ContentEnc struct { allZeroNonce []byte } +// New returns an initialized ContentEnc instance. func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc { - cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen return &ContentEnc{ @@ -62,16 +69,16 @@ func (be *ContentEnc) CipherBS() uint64 { return be.cipherBS } -// DecryptBlocks - Decrypt a number of blocks +// DecryptBlocks decrypts a number of blocks // TODO refactor to three-param for -func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId []byte) ([]byte, error) { +func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileID []byte) ([]byte, error) { cBuf := bytes.NewBuffer(ciphertext) var err error var pBuf bytes.Buffer for cBuf.Len() > 0 { cBlock := cBuf.Next(int(be.cipherBS)) var pBlock []byte - pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileId) + pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileID) if err != nil { break } @@ -85,7 +92,7 @@ func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, file // // Corner case: A full-sized block of all-zero ciphertext bytes is translated // to an all-zero plaintext block, i.e. file hole passtrough. -func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte) ([]byte, error) { +func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []byte) ([]byte, error) { // Empty block? if len(ciphertext) == 0 { @@ -114,7 +121,7 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b // Decrypt var plaintext []byte aData := make([]byte, 8) - aData = append(aData, fileId...) + aData = append(aData, fileID...) binary.BigEndian.PutUint64(aData, blockNo) plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData) diff --git a/internal/contentenc/content_test.go b/internal/contentenc/content_test.go index 70b71fe..e6c610c 100644 --- a/internal/contentenc/content_test.go +++ b/internal/contentenc/content_test.go @@ -63,7 +63,7 @@ func TestCiphertextRange(t *testing.T) { if alignedLength < r.length { t.Errorf("alignedLength=%d is smaller than length=%d", alignedLength, r.length) } - if (alignedOffset-HEADER_LEN)%f.cipherBS != 0 { + if (alignedOffset-HeaderLen)%f.cipherBS != 0 { t.Errorf("alignedOffset=%d is not aligned", alignedOffset) } if r.offset%f.plainBS != 0 && skipBytes == 0 { @@ -81,7 +81,7 @@ func TestBlockNo(t *testing.T) { if b != 0 { t.Errorf("actual: %d", b) } - b = f.CipherOffToBlockNo(HEADER_LEN + f.cipherBS) + b = f.CipherOffToBlockNo(HeaderLen + f.cipherBS) if b != 1 { t.Errorf("actual: %d", b) } diff --git a/internal/contentenc/file_header.go b/internal/contentenc/file_header.go index 1463773..9ae5ae9 100644 --- a/internal/contentenc/file_header.go +++ b/internal/contentenc/file_header.go @@ -12,42 +12,44 @@ import ( ) const ( - // Current On-Disk-Format version + // CurrentVersion is the current On-Disk-Format version CurrentVersion = 2 - HEADER_VERSION_LEN = 2 // uint16 - HEADER_ID_LEN = 16 // 128 bit random file id - HEADER_LEN = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length + headerVersionLen = 2 // uint16 + headerIDLen = 16 // 128 bit random file id + // HeaderLen is the total header length + HeaderLen = headerVersionLen + headerIDLen ) +// FileHeader represents the header stored on each non-empty file. type FileHeader struct { Version uint16 - Id []byte + ID []byte } // Pack - serialize fileHeader object func (h *FileHeader) Pack() []byte { - if len(h.Id) != HEADER_ID_LEN || h.Version != CurrentVersion { + if len(h.ID) != headerIDLen || h.Version != CurrentVersion { panic("FileHeader object not properly initialized") } - buf := make([]byte, HEADER_LEN) - binary.BigEndian.PutUint16(buf[0:HEADER_VERSION_LEN], h.Version) - copy(buf[HEADER_VERSION_LEN:], h.Id) + buf := make([]byte, HeaderLen) + binary.BigEndian.PutUint16(buf[0:headerVersionLen], h.Version) + copy(buf[headerVersionLen:], h.ID) return buf } // ParseHeader - parse "buf" into fileHeader object func ParseHeader(buf []byte) (*FileHeader, error) { - if len(buf) != HEADER_LEN { - return nil, fmt.Errorf("ParseHeader: invalid length: got %d, want %d", len(buf), HEADER_LEN) + if len(buf) != HeaderLen { + return nil, fmt.Errorf("ParseHeader: invalid length: got %d, want %d", len(buf), HeaderLen) } var h FileHeader - h.Version = binary.BigEndian.Uint16(buf[0:HEADER_VERSION_LEN]) + h.Version = binary.BigEndian.Uint16(buf[0:headerVersionLen]) if h.Version != CurrentVersion { return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, CurrentVersion) } - h.Id = buf[HEADER_VERSION_LEN:] + h.ID = buf[headerVersionLen:] return &h, nil } @@ -55,6 +57,6 @@ func ParseHeader(buf []byte) (*FileHeader, error) { func RandomHeader() *FileHeader { var h FileHeader h.Version = CurrentVersion - h.Id = cryptocore.RandBytes(HEADER_ID_LEN) + h.ID = cryptocore.RandBytes(headerIDLen) return &h } diff --git a/internal/contentenc/intrablock.go b/internal/contentenc/intrablock.go index 632e76b..3714e37 100644 --- a/internal/contentenc/intrablock.go +++ b/internal/contentenc/intrablock.go @@ -1,10 +1,10 @@ package contentenc -// intraBlock identifies a part of a file block -type intraBlock struct { - // Block number in the file +// IntraBlock identifies a part of a file block +type IntraBlock struct { + // BlockNo is the block number in the file BlockNo uint64 - // Offset into block payload + // Skip is an offset into the block payload // In forwared mode: block plaintext // In reverse mode: offset into block ciphertext. Takes the header into // account. @@ -17,8 +17,8 @@ type intraBlock struct { fs *ContentEnc } -// isPartial - is the block partial? This means we have to do read-modify-write. -func (ib *intraBlock) IsPartial() bool { +// IsPartial - is the block partial? This means we have to do read-modify-write. +func (ib *IntraBlock) IsPartial() bool { if ib.Skip > 0 || ib.Length < ib.fs.plainBS { return true } @@ -26,17 +26,17 @@ func (ib *intraBlock) IsPartial() bool { } // BlockCipherOff returns the ciphertext offset corresponding to BlockNo -func (ib *intraBlock) BlockCipherOff() (offset uint64) { +func (ib *IntraBlock) BlockCipherOff() (offset uint64) { return ib.fs.BlockNoToCipherOff(ib.BlockNo) } // BlockPlainOff returns the plaintext offset corresponding to BlockNo -func (ib *intraBlock) BlockPlainOff() (offset uint64) { +func (ib *IntraBlock) BlockPlainOff() (offset uint64) { return ib.fs.BlockNoToPlainOff(ib.BlockNo) } // CropBlock - crop a potentially larger plaintext block down to the relevant part -func (ib *intraBlock) CropBlock(d []byte) []byte { +func (ib *IntraBlock) CropBlock(d []byte) []byte { lenHave := len(d) lenWant := int(ib.Skip + ib.Length) if lenHave < lenWant { @@ -45,8 +45,9 @@ func (ib *intraBlock) CropBlock(d []byte) []byte { return d[ib.Skip:lenWant] } -// Ciphertext range corresponding to the sum of all "blocks" (complete blocks) -func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64, length uint64) { +// JointCiphertextRange is the ciphertext range corresponding to the sum of all +// "blocks" (complete blocks) +func (ib *IntraBlock) JointCiphertextRange(blocks []IntraBlock) (offset uint64, length uint64) { firstBlock := blocks[0] lastBlock := blocks[len(blocks)-1] @@ -57,8 +58,9 @@ func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64, return offset, length } -// Plaintext range corresponding to the sum of all "blocks" (complete blocks) -func JointPlaintextRange(blocks []intraBlock) (offset uint64, length uint64) { +// JointPlaintextRange is the plaintext range corresponding to the sum of all +// "blocks" (complete blocks) +func JointPlaintextRange(blocks []IntraBlock) (offset uint64, length uint64) { firstBlock := blocks[0] lastBlock := blocks[len(blocks)-1] diff --git a/internal/contentenc/offsets.go b/internal/contentenc/offsets.go index 61939a9..97d7116 100644 --- a/internal/contentenc/offsets.go +++ b/internal/contentenc/offsets.go @@ -8,43 +8,43 @@ import ( // Contentenc methods that translate offsets between ciphertext and plaintext -// get the block number at plain-text offset +// PlainOffToBlockNo converts a plaintext offset to the ciphertext block number. func (be *ContentEnc) PlainOffToBlockNo(plainOffset uint64) uint64 { return plainOffset / be.plainBS } -// get the block number at cipher-text offset +// CipherOffToBlockNo converts the ciphertext offset to the plaintext block number. func (be *ContentEnc) CipherOffToBlockNo(cipherOffset uint64) uint64 { - if cipherOffset < HEADER_LEN { + if cipherOffset < HeaderLen { log.Panicf("BUG: offset %d is inside the file header", cipherOffset) } - return (cipherOffset - HEADER_LEN) / be.cipherBS + return (cipherOffset - HeaderLen) / be.cipherBS } -// get ciphertext offset of block "blockNo" +// BlockNoToCipherOff gets the ciphertext offset of block "blockNo" func (be *ContentEnc) BlockNoToCipherOff(blockNo uint64) uint64 { - return HEADER_LEN + blockNo*be.cipherBS + return HeaderLen + blockNo*be.cipherBS } -// get plaintext offset of block "blockNo" +// BlockNoToPlainOff gets the plaintext offset of block "blockNo" func (be *ContentEnc) BlockNoToPlainOff(blockNo uint64) uint64 { return blockNo * be.plainBS } -// PlainSize - calculate plaintext size from ciphertext size +// CipherSizeToPlainSize calculates the plaintext size from a ciphertext size func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 { // Zero-sized files stay zero-sized if cipherSize == 0 { return 0 } - if cipherSize == HEADER_LEN { + if cipherSize == HeaderLen { tlog.Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize) return 0 } - if cipherSize < HEADER_LEN { - tlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HEADER_LEN) + if cipherSize < HeaderLen { + tlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HeaderLen) return 0 } @@ -52,12 +52,12 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 { blockNo := be.CipherOffToBlockNo(cipherSize - 1) blockCount := blockNo + 1 - overhead := be.BlockOverhead()*blockCount + HEADER_LEN + overhead := be.BlockOverhead()*blockCount + HeaderLen return cipherSize - overhead } -// CipherSize - calculate ciphertext size from plaintext size +// PlainSizeToCipherSize calculates the ciphertext size from a plaintext size func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 { // Zero-sized files stay zero-sized if plainSize == 0 { @@ -68,16 +68,16 @@ func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 { blockNo := be.PlainOffToBlockNo(plainSize - 1) blockCount := blockNo + 1 - overhead := be.BlockOverhead()*blockCount + HEADER_LEN + overhead := be.BlockOverhead()*blockCount + HeaderLen return plainSize + overhead } -// Split a plaintext byte range into (possibly partial) blocks +// ExplodePlainRange splits a plaintext byte range into (possibly partial) blocks // Returns an empty slice if length == 0. -func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlock { - var blocks []intraBlock - var nextBlock intraBlock +func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []IntraBlock { + var blocks []IntraBlock + var nextBlock IntraBlock nextBlock.fs = be for length > 0 { @@ -94,11 +94,11 @@ func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlo return blocks } -// Split a ciphertext byte range into (possibly partial) blocks -// This is used in reverse mode when reading files -func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []intraBlock { - var blocks []intraBlock - var nextBlock intraBlock +// ExplodeCipherRange splits a ciphertext byte range into (possibly partial) +// blocks This is used in reverse mode when reading files +func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []IntraBlock { + var blocks []IntraBlock + var nextBlock IntraBlock nextBlock.fs = be for length > 0 { @@ -120,10 +120,12 @@ func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []intraBl return blocks } +// BlockOverhead returns the per-block overhead. func (be *ContentEnc) BlockOverhead() uint64 { return be.cipherBS - be.plainBS } +// MinUint64 returns the minimum of two uint64 values. func MinUint64(x uint64, y uint64) uint64 { if x < y { return x |