From b764917cd5c1b1d61b8ce08e7af0b29793fbbb80 Mon Sep 17 00:00:00 2001 From: Valient Gough Date: Sat, 1 Oct 2016 21:14:18 -0700 Subject: [PATCH] lint fixes --- cli_args.go | 2 +- gocryptfs-xray/xray_main.go | 16 ++-- init_dir.go | 8 +- internal/configfile/config_file.go | 24 +++--- internal/configfile/feature_flags.go | 18 +++-- internal/configfile/kdf.go | 14 ++-- internal/configfile/kdf_test.go | 2 +- internal/contentenc/content.go | 27 ++++--- internal/contentenc/content_test.go | 4 +- internal/contentenc/file_header.go | 30 ++++---- internal/contentenc/intrablock.go | 28 +++---- internal/contentenc/offsets.go | 48 ++++++------ internal/cryptocore/cryptocore.go | 17 +++-- internal/cryptocore/nonce.go | 4 +- internal/fusefrontend/args.go | 2 +- internal/fusefrontend/file.go | 9 ++- .../fusefrontend/file_allocate_truncate.go | 76 ++++++++++--------- internal/fusefrontend/fs.go | 24 +++++- internal/fusefrontend/fs_dir.go | 3 + internal/fusefrontend_reverse/ino_map.go | 2 +- .../fusefrontend_reverse/reverse_longnames.go | 4 +- internal/fusefrontend_reverse/rfile.go | 10 +-- internal/fusefrontend_reverse/rfs.go | 9 ++- internal/nametransform/diriv.go | 6 +- internal/nametransform/longnames.go | 6 +- internal/nametransform/names.go | 6 +- internal/prefer_openssl/prefer_go1.5.go | 1 + internal/readpassword/read.go | 8 +- internal/siv_aead/siv_aead.go | 11 ++- internal/stupidgcm/stupidgcm.go | 10 ++- internal/syscallcompat/sys_linux.go | 5 ++ internal/tlog/log.go | 19 ++++- main.go | 50 ++++++------ tests/test_helpers/helpers.go | 26 ++++--- 34 files changed, 316 insertions(+), 213 deletions(-) diff --git a/cli_args.go b/cli_args.go index 6d42c4e..69178d2 100644 --- a/cli_args.go +++ b/cli_args.go @@ -71,7 +71,7 @@ func parseCliOpts() (args argContainer) { args.openssl, err = strconv.ParseBool(opensslAuto) if err != nil { tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err) - os.Exit(ERREXIT_USAGE) + os.Exit(ErrExitUsage) } } diff --git a/gocryptfs-xray/xray_main.go b/gocryptfs-xray/xray_main.go index d58f1a6..0073c3d 100644 --- a/gocryptfs-xray/xray_main.go +++ b/gocryptfs-xray/xray_main.go @@ -12,8 +12,8 @@ import ( ) const ( - IVLen = contentenc.DefaultIVBits / 8 - blockSize = contentenc.DefaultBS + IVLen + cryptocore.AuthTagLen + ivLen = contentenc.DefaultIVBits / 8 + blockSize = contentenc.DefaultBS + ivLen + cryptocore.AuthTagLen ) func errExit(err error) { @@ -22,7 +22,7 @@ func errExit(err error) { } func prettyPrintHeader(h *contentenc.FileHeader) { - id := hex.EncodeToString(h.Id) + id := hex.EncodeToString(h.ID) fmt.Printf("Header: Version: %d, Id: %s\n", h.Version, id) } @@ -38,13 +38,13 @@ func main() { errExit(err) } - headerBytes := make([]byte, contentenc.HEADER_LEN) + headerBytes := make([]byte, contentenc.HeaderLen) n, err := fd.ReadAt(headerBytes, 0) if err == io.EOF && n == 0 { fmt.Println("empty file") os.Exit(0) } else if err == io.EOF { - fmt.Printf("incomplete file header: read %d bytes, want %d\n", n, contentenc.HEADER_LEN) + fmt.Printf("incomplete file header: read %d bytes, want %d\n", n, contentenc.HeaderLen) os.Exit(1) } else if err != nil { errExit(err) @@ -57,8 +57,8 @@ func main() { var i int64 for i = 0; ; i++ { blockLen := int64(blockSize) - off := contentenc.HEADER_LEN + i*blockSize - iv := make([]byte, IVLen) + off := contentenc.HeaderLen + i*blockSize + iv := make([]byte, ivLen) _, err := fd.ReadAt(iv, off) if err == io.EOF { break @@ -76,7 +76,7 @@ func main() { if err2 != nil { errExit(err2) } - blockLen = (fi.Size() - contentenc.HEADER_LEN) % blockSize + blockLen = (fi.Size() - contentenc.HeaderLen) % blockSize } else if err != nil { errExit(err) } diff --git a/init_dir.go b/init_dir.go index 7f3d333..1f91e7b 100644 --- a/init_dir.go +++ b/init_dir.go @@ -22,13 +22,13 @@ func initDir(args *argContainer) { _, err = os.Stat(args.config) if err == nil { tlog.Fatal.Printf("Config file %q already exists", args.config) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } } else { err = checkDirEmpty(args.cipherdir) if err != nil { tlog.Fatal.Printf("Invalid cipherdir: %v", err) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } } // Choose password for config file @@ -40,7 +40,7 @@ func initDir(args *argContainer) { err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv) if err != nil { tlog.Fatal.Println(err) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } // Forward mode with filename encryption enabled needs a gocryptfs.diriv // in the root dir @@ -48,7 +48,7 @@ func initDir(args *argContainer) { err = nametransform.WriteDirIV(args.cipherdir) if err != nil { tlog.Fatal.Println(err) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } } mountArgs := "" diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index 9152523..79960e4 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -14,31 +14,35 @@ import ( import "os" const ( + // ConfDefaultName is the default configuration file name. // The dot "." is not used in base64url (RFC4648), hence // we can never clash with an encrypted file. ConfDefaultName = "gocryptfs.conf" - // In reverse mode, the config file gets stored next to the plain-text - // files. Make it hidden (start with dot) to not annoy the user. + // ConfReverseName is the default configuration file name in reverse mode, + // the config file gets stored next to the plain-text files. Make it hidden + // (start with dot) to not annoy the user. ConfReverseName = ".gocryptfs.reverse.conf" ) +// ConfFile is the content of a config file. type ConfFile struct { - // gocryptfs version string + // Creator is the gocryptfs version string. // This only documents the config file for humans who look at it. The actual // technical info is contained in FeatureFlags. Creator string - // Encrypted AES key, unlocked using a password hashed with scrypt + // EncryptedKey holds an encrypted AES key, unlocked using a password + // hashed with scrypt EncryptedKey []byte - // Stores parameters for scrypt hashing (key derivation) - ScryptObject scryptKdf - // The On-Disk-Format version this filesystem uses + // ScryptObject stores parameters for scrypt hashing (key derivation) + ScryptObject ScryptKDF + // Version is the On-Disk-Format version this filesystem uses Version uint16 - // List of feature flags this filesystem has enabled. + // FeatureFlags is a list of feature flags this filesystem has enabled. // If gocryptfs encounters a feature flag it does not support, it will refuse // mounting. This mechanism is analogous to the ext4 feature flags that are // stored in the superblock. FeatureFlags []string - // File the config is saved to. Not exported to JSON. + // Filename is the name of the config file. Not exported to JSON. filename string } @@ -162,7 +166,7 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) { // cf.ScryptObject. func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) { // Generate derived key from password - cf.ScryptObject = NewScryptKdf(logN) + cf.ScryptObject = NewScryptKDF(logN) scryptHash := cf.ScryptObject.DeriveKey(password) // Lock master key using password-based key diff --git a/internal/configfile/feature_flags.go b/internal/configfile/feature_flags.go index ad7bec1..d3601b1 100644 --- a/internal/configfile/feature_flags.go +++ b/internal/configfile/feature_flags.go @@ -3,16 +3,24 @@ package configfile type flagIota int const ( + // FlagPlaintextNames indicates that filenames are unencrypted. FlagPlaintextNames flagIota = iota + // FlagDirIV indicates that a per-directory IV file is used. FlagDirIV + // FlagEMENames indicates EME (ECB-Mix-ECB) filename encryption. + // This flag is mandatory since gocryptfs v1.0. FlagEMENames + // FlagGCMIV128 indicates 128-bit GCM IVs. + // This flag is mandatory since gocryptfs v1.0. FlagGCMIV128 + // FlagLongNames allows file names longer than 176 bytes. FlagLongNames + // FlagAESSIV selects an AES-SIV based crypto backend. FlagAESSIV ) // knownFlags stores the known feature flags and their string representation -var knownFlags map[flagIota]string = map[flagIota]string{ +var knownFlags = map[flagIota]string{ FlagPlaintextNames: "PlaintextNames", FlagDirIV: "DirIV", FlagEMENames: "EMENames", @@ -22,7 +30,7 @@ var knownFlags map[flagIota]string = map[flagIota]string{ } // Filesystems that do not have these feature flags set are deprecated. -var requiredFlagsNormal []flagIota = []flagIota{ +var requiredFlagsNormal = []flagIota{ FlagDirIV, FlagEMENames, FlagGCMIV128, @@ -30,11 +38,11 @@ var requiredFlagsNormal []flagIota = []flagIota{ // Filesystems without filename encryption obviously don't have or need the // filename related feature flags. -var requiredFlagsPlaintextNames []flagIota = []flagIota{ +var requiredFlagsPlaintextNames = []flagIota{ FlagGCMIV128, } -// isFeatureFlagKnown verifies that we understand a feature flag +// isFeatureFlagKnown verifies that we understand a feature flag. func (cf *ConfFile) isFeatureFlagKnown(flag string) bool { for _, knownFlag := range knownFlags { if knownFlag == flag { @@ -44,7 +52,7 @@ func (cf *ConfFile) isFeatureFlagKnown(flag string) bool { return false } -// isFeatureFlagSet - is the feature flag "flagWant" enabled? +// IsFeatureFlagSet returns true if the feature flag "flagWant" is enabled. func (cf *ConfFile) IsFeatureFlagSet(flagWant flagIota) bool { flagString := knownFlags[flagWant] for _, flag := range cf.FeatureFlags { diff --git a/internal/configfile/kdf.go b/internal/configfile/kdf.go index ca87975..31bcbe4 100644 --- a/internal/configfile/kdf.go +++ b/internal/configfile/kdf.go @@ -12,12 +12,14 @@ import ( ) const ( + // ScryptDefaultLogN is the default scrypt logN configuration parameter. // 1 << 16 uses 64MB of memory, // takes 4 seconds on my Atom Z3735F netbook ScryptDefaultLogN = 16 ) -type scryptKdf struct { +// ScryptKDF is an instance of the scrypt key deriviation function. +type ScryptKDF struct { Salt []byte N int R int @@ -25,8 +27,9 @@ type scryptKdf struct { KeyLen int } -func NewScryptKdf(logN int) scryptKdf { - var s scryptKdf +// NewScryptKDF returns a new instance of ScryptKDF. +func NewScryptKDF(logN int) ScryptKDF { + var s ScryptKDF s.Salt = cryptocore.RandBytes(cryptocore.KeyLen) if logN <= 0 { s.N = 1 << ScryptDefaultLogN @@ -43,7 +46,8 @@ func NewScryptKdf(logN int) scryptKdf { return s } -func (s *scryptKdf) DeriveKey(pw string) []byte { +// DeriveKey returns a new key from a supplied password. +func (s *ScryptKDF) DeriveKey(pw string) []byte { k, err := scrypt.Key([]byte(pw), s.Salt, s.N, s.R, s.P, s.KeyLen) if err != nil { log.Panicf("DeriveKey failed: %v", err) @@ -53,6 +57,6 @@ func (s *scryptKdf) DeriveKey(pw string) []byte { // LogN - N is saved as 2^LogN, but LogN is much easier to work with. // This function gives you LogN = Log2(N). -func (s *scryptKdf) LogN() int { +func (s *ScryptKDF) LogN() int { return int(math.Log2(float64(s.N)) + 0.5) } diff --git a/internal/configfile/kdf_test.go b/internal/configfile/kdf_test.go index bc095ab..c1a656a 100644 --- a/internal/configfile/kdf_test.go +++ b/internal/configfile/kdf_test.go @@ -21,7 +21,7 @@ ok github.com/rfjakob/gocryptfs/cryptfs 18.772s */ func benchmarkScryptN(n int, b *testing.B) { - kdf := NewScryptKdf(n) + kdf := NewScryptKDF(n) for i := 0; i < b.N; i++ { kdf.DeriveKey("test") } 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 diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 05c0704..7cb5c95 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -12,18 +12,25 @@ import ( "github.com/rfjakob/gocryptfs/internal/stupidgcm" ) +// BackendTypeEnum indicates the type of backend in use. type BackendTypeEnum int const ( - KeyLen = 32 // AES-256 + // KeyLen is the cipher key length in bytes. 32 for AES-256. + KeyLen = 32 + // AuthTagLen is the length of a GCM auth tag in bytes. AuthTagLen = 16 - _ = iota // Skip zero + _ = iota // Skip zero + // BackendOpenSSL specifies the OpenSSL backend. BackendOpenSSL BackendTypeEnum = iota - BackendGoGCM BackendTypeEnum = iota - BackendAESSIV BackendTypeEnum = iota + // BackendGoGCM specifies the Go based GCM backend. + BackendGoGCM BackendTypeEnum = iota + // BackendAESSIV specifies an AESSIV backend. + BackendAESSIV BackendTypeEnum = iota ) +// CryptoCore is the low level crypto implementation. type CryptoCore struct { // AES-256 block cipher. This is used for EME filename encryption. BlockCipher cipher.Block @@ -36,7 +43,7 @@ type CryptoCore struct { IVLen int } -// "New" returns a new CryptoCore object or panics. +// New returns a new CryptoCore object or panics. // // Even though the "GCMIV128" feature flag is now mandatory, we must still // support 96-bit IVs here because they are used for encrypting the master diff --git a/internal/cryptocore/nonce.go b/internal/cryptocore/nonce.go index 6b0c31d..973d2d8 100644 --- a/internal/cryptocore/nonce.go +++ b/internal/cryptocore/nonce.go @@ -10,7 +10,7 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) -// Get "n" random bytes from /dev/urandom or panic +// RandBytes gets "n" random bytes from /dev/urandom or panics func RandBytes(n int) []byte { b := make([]byte, n) _, err := rand.Read(b) @@ -20,7 +20,7 @@ func RandBytes(n int) []byte { return b } -// Return a secure random uint64 +// RandUint64 returns a secure random uint64 func RandUint64() uint64 { b := RandBytes(8) return binary.BigEndian.Uint64(b) diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index d0e1835..64ca899 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -4,7 +4,7 @@ import ( "github.com/rfjakob/gocryptfs/internal/cryptocore" ) -// Container for arguments that are passed from main() to fusefrontend +// Args is a container for arguments that are passed from main() to fusefrontend type Args struct { Masterkey []byte Cipherdir string diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index a04b6af..ab025d3 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -45,6 +45,7 @@ type file struct { loopbackFile nodefs.File } +// NewFile returns a new go-fuse File instance. func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (nodefs.File, fuse.Status) { var st syscall.Stat_t err := syscall.Fstat(int(fd.Fd()), &st) @@ -80,7 +81,7 @@ func (f *file) SetInode(n *nodefs.Inode) { // // Returns io.EOF if the file is empty func (f *file) readHeader() error { - buf := make([]byte, contentenc.HEADER_LEN) + buf := make([]byte, contentenc.HeaderLen) _, err := f.fd.ReadAt(buf, 0) if err != nil { return err @@ -100,7 +101,7 @@ func (f *file) createHeader() error { buf := h.Pack() // Prevent partially written (=corrupt) header by preallocating the space beforehand - err := syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HEADER_LEN) + err := syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HeaderLen) if err != nil { tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error()) return err @@ -159,7 +160,7 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) { tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n) // Decrypt it - plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, f.header.Id) + plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, f.header.ID) if err != nil { curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) cipherOff := f.contentEnc.BlockNoToCipherOff(curruptBlockNo) @@ -256,7 +257,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) { // Encrypt blockOffset := b.BlockCipherOff() - blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.Id) + blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.ID) tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d", f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo) diff --git a/internal/fusefrontend/file_allocate_truncate.go b/internal/fusefrontend/file_allocate_truncate.go index f22fa4b..650e1db 100644 --- a/internal/fusefrontend/file_allocate_truncate.go +++ b/internal/fusefrontend/file_allocate_truncate.go @@ -14,7 +14,10 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) +// FALLOC_DEFAULT is a "normal" fallocate operation const FALLOC_DEFAULT = 0x00 + +// FALLOC_FL_KEEP_SIZE allocates disk space while not modifying the file size const FALLOC_FL_KEEP_SIZE = 0x01 // Only warn once @@ -116,11 +119,12 @@ func (f *file) Truncate(newSize uint64) fuse.Status { oldSize, err := f.statPlainSize() if err != nil { return fuse.ToStatus(err) - } else { - oldB := float32(oldSize) / float32(f.contentEnc.PlainBS()) - newB := float32(newSize) / float32(f.contentEnc.PlainBS()) - tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize) } + + oldB := float32(oldSize) / float32(f.contentEnc.PlainBS()) + newB := float32(newSize) / float32(f.contentEnc.PlainBS()) + tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize) + // File size stays the same - nothing to do if newSize == oldSize { return fuse.OK @@ -128,34 +132,34 @@ func (f *file) Truncate(newSize uint64) fuse.Status { // File grows if newSize > oldSize { return f.truncateGrowFile(oldSize, newSize) - } else { - // File shrinks - blockNo := f.contentEnc.PlainOffToBlockNo(newSize) - cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo) - plainOff := f.contentEnc.BlockNoToPlainOff(blockNo) - lastBlockLen := newSize - plainOff - var data []byte - if lastBlockLen > 0 { - var status fuse.Status - data, status = f.doRead(plainOff, lastBlockLen) - if status != fuse.OK { - tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err) - return status - } - } - // Truncate down to the last complete block - err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff)) - if err != nil { - tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err) - return fuse.ToStatus(err) - } - // Append partial block - if lastBlockLen > 0 { - _, status := f.doWrite(data, int64(plainOff)) + } + + // File shrinks + blockNo := f.contentEnc.PlainOffToBlockNo(newSize) + cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo) + plainOff := f.contentEnc.BlockNoToPlainOff(blockNo) + lastBlockLen := newSize - plainOff + var data []byte + if lastBlockLen > 0 { + var status fuse.Status + data, status = f.doRead(plainOff, lastBlockLen) + if status != fuse.OK { + tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err) return status } - return fuse.OK } + // Truncate down to the last complete block + err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff)) + if err != nil { + tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err) + return fuse.ToStatus(err) + } + // Append partial block + if lastBlockLen > 0 { + _, status := f.doWrite(data, int64(plainOff)) + return status + } + return fuse.OK } // statPlainSize stats the file and returns the plaintext size @@ -198,12 +202,12 @@ func (f *file) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Statu off := lastBlock.BlockPlainOff() _, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip)) return status - } else { - off := lastBlock.BlockCipherOff() - err = syscall.Ftruncate(f.intFd(), int64(off+f.contentEnc.CipherBS())) - if err != nil { - tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err) - } - return fuse.ToStatus(err) } + + off := lastBlock.BlockCipherOff() + err = syscall.Ftruncate(f.intFd(), int64(off+f.contentEnc.CipherBS())) + if err != nil { + tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err) + } + return fuse.ToStatus(err) } diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index 62146a2..26a5b5e 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -22,6 +22,7 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) +// FS implements the go-fuse virtual filesystem interface. type FS struct { pathfs.FileSystem // loopbackFileSystem, see go-fuse/fuse/pathfs/loopback.go args Args // Stores configuration arguments @@ -35,7 +36,9 @@ type FS struct { contentEnc *contentenc.ContentEnc } -// Encrypted FUSE overlay filesystem +var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented. + +// NewFS returns a new encrypted FUSE overlay filesystem. func NewFS(args Args) *FS { cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) @@ -49,6 +52,7 @@ func NewFS(args Args) *FS { } } +// GetAttr implements pathfs.Filesystem. func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { tlog.Debug.Printf("FS.GetAttr('%s')", name) if fs.isFiltered(name) { @@ -85,6 +89,7 @@ func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int, writeOnly bool) { return newFlags, writeOnly } +// Open implements pathfs.Filesystem. func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { if fs.isFiltered(path) { return nil, fuse.EPERM @@ -104,6 +109,7 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n return NewFile(f, writeOnly, fs.contentEnc) } +// Create implements pathfs.Filesystem. func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) { if fs.isFiltered(path) { return nil, fuse.EPERM @@ -156,6 +162,7 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte return NewFile(fd, writeOnly, fs.contentEnc) } +// Chmod implements pathfs.Filesystem. func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM @@ -170,6 +177,7 @@ func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse. return fuse.ToStatus(err) } +// Chown implements pathfs.Filesystem. func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM @@ -181,6 +189,7 @@ func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) return fuse.ToStatus(os.Lchown(cPath, int(uid), int(gid))) } +// Mknod implements pathfs.Filesystem. func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM @@ -217,6 +226,7 @@ func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) return fs.FileSystem.Mknod(cPath, mode, dev, context) } +// Truncate implements pathfs.Filesystem. // Support truncate(2) by opening the file and calling ftruncate(2) // While the glibc "truncate" wrapper seems to always use ftruncate, fsstress from // xfstests uses this a lot by calling "truncate64" directly. @@ -230,6 +240,7 @@ func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code return code } +// Utimens implements pathfs.Filesystem. func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM @@ -241,6 +252,7 @@ func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Con return fs.FileSystem.Utimens(cPath, a, m, context) } +// StatFs implements pathfs.Filesystem. func (fs *FS) StatFs(path string) *fuse.StatfsOut { if fs.isFiltered(path) { return nil @@ -252,6 +264,7 @@ func (fs *FS) StatFs(path string) *fuse.StatfsOut { return fs.FileSystem.StatFs(cPath) } +// Readlink implements pathfs.Filesystem. func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status fuse.Status) { cPath, err := fs.getBackingPath(path) if err != nil { @@ -278,6 +291,7 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f return string(target), fuse.OK } +// Unlink implements pathfs.Filesystem. func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM @@ -312,6 +326,7 @@ func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) { return fuse.ToStatus(err) } +// Symlink implements pathfs.Filesystem. func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) { tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName) if fs.isFiltered(linkName) { @@ -359,6 +374,7 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co return fuse.ToStatus(err) } +// Rename implements pathfs.Filesystem. func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(newPath) { return fuse.EPERM @@ -437,6 +453,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod return fuse.OK } +// Link implements pathfs.Filesystem. func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(newPath) { return fuse.EPERM @@ -474,6 +491,7 @@ func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code return fuse.ToStatus(os.Link(cOldPath, cNewPath)) } +// Access implements pathfs.Filesystem. func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM @@ -485,18 +503,22 @@ func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse return fuse.ToStatus(syscall.Access(cPath, mode)) } +// GetXAttr implements pathfs.Filesystem. func (fs *FS) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) { return nil, fuse.ENOSYS } +// SetXAttr implements pathfs.Filesystem. func (fs *FS) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status { return fuse.ENOSYS } +// ListXAttr implements pathfs.Filesystem. func (fs *FS) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) { return nil, fuse.ENOSYS } +// RemoveXAttr implements pathfs.Filesystem. func (fs *FS) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status { return fuse.ENOSYS } diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index ab97932..a3b2ccf 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -40,6 +40,7 @@ func (fs *FS) mkdirWithIv(cPath string, mode uint32) error { return err } +// Mkdir implements pathfs.FileSystem func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(newPath) { return fuse.EPERM @@ -97,6 +98,7 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu return fuse.OK } +// Rmdir implements pathfs.FileSystem func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { cPath, err := fs.getBackingPath(path) if err != nil { @@ -215,6 +217,7 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { return fuse.OK } +// OpenDir implements pathfs.FileSystem func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { tlog.Debug.Printf("OpenDir(%s)", dirName) cDirName, err := fs.encryptPath(dirName) diff --git a/internal/fusefrontend_reverse/ino_map.go b/internal/fusefrontend_reverse/ino_map.go index 5217732..9412343 100644 --- a/internal/fusefrontend_reverse/ino_map.go +++ b/internal/fusefrontend_reverse/ino_map.go @@ -4,7 +4,7 @@ import ( "sync/atomic" ) -func NewInoGen() *inoGenT { +func newInoGen() *inoGenT { var ino uint64 = 1 return &inoGenT{&ino} } diff --git a/internal/fusefrontend_reverse/reverse_longnames.go b/internal/fusefrontend_reverse/reverse_longnames.go index b3d21c9..5ad95b1 100644 --- a/internal/fusefrontend_reverse/reverse_longnames.go +++ b/internal/fusefrontend_reverse/reverse_longnames.go @@ -70,9 +70,9 @@ func (rfs *reverseFS) findLongnameParent(dir string, dirIV []byte, longname stri } if hit == "" { return "", syscall.ENOENT - } else { - return hit, nil } + + return hit, nil } func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) { diff --git a/internal/fusefrontend_reverse/rfile.go b/internal/fusefrontend_reverse/rfile.go index 2656b7b..5606e70 100644 --- a/internal/fusefrontend_reverse/rfile.go +++ b/internal/fusefrontend_reverse/rfile.go @@ -39,7 +39,7 @@ func (rfs *reverseFS) NewFile(relPath string, flags uint32) (nodefs.File, fuse.S id := derivePathIV(relPath, ivPurposeFileID) header := contentenc.FileHeader{ Version: contentenc.CurrentVersion, - Id: id, + ID: id, } return &reverseFile{ File: nodefs.NewDefaultFile(), @@ -58,7 +58,7 @@ func (rf *reverseFile) GetAttr(*fuse.Attr) fuse.Status { // encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks. // "plaintext" must already be block-aligned. -func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, fileId []byte, block0IV []byte) []byte { +func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, fileID []byte, block0IV []byte) []byte { nonce := make([]byte, len(block0IV)) copy(nonce, block0IV) block0IVlow := binary.BigEndian.Uint64(block0IV[8:]) @@ -70,7 +70,7 @@ func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, file for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ { binary.BigEndian.PutUint64(nonceLow, block0IVlow+blockNo) inBlock := inBuf.Next(bs) - outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileId, nonce) + outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileID, nonce) outBuf.Write(outBlock) } return outBuf.Bytes() @@ -95,7 +95,7 @@ func (rf *reverseFile) readBackingFile(off uint64, length uint64) (out []byte, e plaintext = plaintext[0:n] // Encrypt blocks - ciphertext := rf.encryptBlocks(plaintext, blocks[0].BlockNo, rf.header.Id, rf.block0IV) + ciphertext := rf.encryptBlocks(plaintext, blocks[0].BlockNo, rf.header.ID, rf.block0IV) // Crop down to the relevant part lenHave := len(ciphertext) @@ -118,7 +118,7 @@ func (rf *reverseFile) Read(buf []byte, ioff int64) (resultData fuse.ReadResult, var header []byte // Synthesize file header - if off < contentenc.HEADER_LEN { + if off < contentenc.HeaderLen { header = rf.header.Pack() // Truncate to requested part end := int(off) + len(buf) diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index e49807b..ab65ba3 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -20,6 +20,7 @@ import ( ) const ( + // DirIVMode is the mode to use for Dir IV files. DirIVMode = syscall.S_IFREG | 0400 ) @@ -42,8 +43,10 @@ type reverseFS struct { inoMapLock sync.Mutex } -// Encrypted FUSE overlay filesystem -func NewFS(args fusefrontend.Args) *reverseFS { +var _ pathfs.FileSystem = &reverseFS{} + +// NewFS returns an encrypted FUSE overlay filesystem +func NewFS(args fusefrontend.Args) pathfs.FileSystem { cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) nameTransform := nametransform.New(cryptoCore, args.LongNames) @@ -55,7 +58,7 @@ func NewFS(args fusefrontend.Args) *reverseFS { args: args, nameTransform: nameTransform, contentEnc: contentEnc, - inoGen: NewInoGen(), + inoGen: newInoGen(), inoMap: map[devIno]uint64{}, } } diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go index 9c3c1d1..6687b40 100644 --- a/internal/nametransform/diriv.go +++ b/internal/nametransform/diriv.go @@ -14,10 +14,10 @@ import ( ) const ( - // identical to AES block size + // DirIVLen is identical to AES block size DirIVLen = 16 - // dirIV is stored in this file. Exported because we have to ignore this - // name in directory listing. + // DirIVFilename is the filename used to store directory IV. + // Exported because we have to ignore this name in directory listing. DirIVFilename = "gocryptfs.diriv" ) diff --git a/internal/nametransform/longnames.go b/internal/nametransform/longnames.go index e61e21b..be00bb4 100644 --- a/internal/nametransform/longnames.go +++ b/internal/nametransform/longnames.go @@ -14,6 +14,7 @@ import ( ) const ( + // LongNameSuffix is the suffix used for files with long names. // Files with long names are stored in two files: // gocryptfs.longname.[sha256] <--- File content, prefix = gocryptfs.longname. // gocryptfs.longname.[sha256].name <--- File name, suffix = .name @@ -31,12 +32,13 @@ func HashLongName(name string) string { // Values returned by IsLongName const ( - // File that stores the file content. + // LongNameContent is the file that stores the file content. // Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro= LongNameContent = iota - // File that stores the full encrypted filename. + // LongNameFilename is the file that stores the full encrypted filename. // Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=.name LongNameFilename = iota + // LongNameNone is used when the file does not have a long name. // Example: i1bpTaVLZq7sRNA9mL_2Ig== LongNameNone = iota ) diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go index 4850138..05ae75e 100644 --- a/internal/nametransform/names.go +++ b/internal/nametransform/names.go @@ -1,4 +1,4 @@ -// Package namtransforms encrypts and decrypts filenames. +// Package nametransform encrypts and decrypts filenames. package nametransform import ( @@ -12,12 +12,14 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) +// NameTransform is used to transform filenames. type NameTransform struct { cryptoCore *cryptocore.CryptoCore longNames bool DirIVCache dirIVCache } +// New returns a new NameTransform instance. func New(c *cryptocore.CryptoCore, longNames bool) *NameTransform { return &NameTransform{ cryptoCore: c, @@ -51,7 +53,7 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error return plain, err } -// encryptName - encrypt "plainName", return base64-encoded "cipherName64". +// EncryptName encrypts "plainName", returns a base64-encoded "cipherName64". // Used internally by EncryptPathDirIV(). // The encryption is either CBC or EME, depending on "useEME". // diff --git a/internal/prefer_openssl/prefer_go1.5.go b/internal/prefer_openssl/prefer_go1.5.go index 7095314..b89e495 100644 --- a/internal/prefer_openssl/prefer_go1.5.go +++ b/internal/prefer_openssl/prefer_go1.5.go @@ -7,6 +7,7 @@ import ( "github.com/rfjakob/gocryptfs/internal/stupidgcm" ) +// PreferOpenSSL returns true if OpenSSL should be used. func PreferOpenSSL() bool { if stupidgcm.BuiltWithoutOpenssl { return false diff --git a/internal/readpassword/read.go b/internal/readpassword/read.go index 653868e..c1fc3de 100644 --- a/internal/readpassword/read.go +++ b/internal/readpassword/read.go @@ -17,8 +17,8 @@ const ( exitCode = 9 ) -// Once() tries to get a password from the user, either from the terminal, -// extpass or stdin. +// Once tries to get a password from the user, either from the terminal, extpass +// or stdin. func Once(extpass string) string { if extpass != "" { return readPasswordExtpass(extpass) @@ -29,8 +29,8 @@ func Once(extpass string) string { return readPasswordTerminal("Password: ") } -// Twice() is the same as Once but will prompt twice if we get -// the password from the terminal. +// Twice is the same as Once but will prompt twice if we get the password from +// the terminal. func Twice(extpass string) string { if extpass != "" { return readPasswordExtpass(extpass) diff --git a/internal/siv_aead/siv_aead.go b/internal/siv_aead/siv_aead.go index a0ed882..c68ecdb 100644 --- a/internal/siv_aead/siv_aead.go +++ b/internal/siv_aead/siv_aead.go @@ -3,6 +3,8 @@ package siv_aead import ( + "crypto/cipher" + "github.com/jacobsa/crypto/siv" ) @@ -10,7 +12,10 @@ type sivAead struct { key []byte } -func New(key []byte) *sivAead { +var _ cipher.AEAD = &sivAead{} + +// New returns a new cipher.AEAD implementation. +func New(key []byte) cipher.AEAD { return &sivAead{ key: key, } @@ -25,7 +30,7 @@ func (s *sivAead) Overhead() int { return 16 } -// Seal - encrypt "in" using "nonce" and "authData" and append the result to "dst" +// Seal encrypts "in" using "nonce" and "authData" and append the result to "dst" func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte { if len(nonce) != 16 { // SIV supports any nonce size, but in gocryptfs we exclusively use 16. @@ -43,7 +48,7 @@ func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte { return out } -// Open - decrypt "in" using "nonce" and "authData" and append the result to "dst" +// Open decrypts "in" using "nonce" and "authData" and append the result to "dst" func (s *sivAead) Open(dst, nonce, ciphertext, authData []byte) ([]byte, error) { if len(nonce) != 16 { // SIV supports any nonce size, but in gocryptfs we exclusively use 16. diff --git a/internal/stupidgcm/stupidgcm.go b/internal/stupidgcm/stupidgcm.go index db9e6ef..58027f8 100644 --- a/internal/stupidgcm/stupidgcm.go +++ b/internal/stupidgcm/stupidgcm.go @@ -9,6 +9,7 @@ package stupidgcm import "C" import ( + "crypto/cipher" "fmt" "log" "unsafe" @@ -28,7 +29,10 @@ type stupidGCM struct { key []byte } -func New(key []byte) stupidGCM { +var _ cipher.AEAD = &stupidGCM{} + +// New returns a new cipher.AEAD implementation.. +func New(key []byte) cipher.AEAD { if len(key) != keyLen { log.Panicf("Only %d-byte keys are supported", keyLen) } @@ -43,7 +47,7 @@ func (g stupidGCM) Overhead() int { return tagLen } -// Seal - encrypt "in" using "iv" and "authData" and append the result to "dst" +// Seal encrypts "in" using "iv" and "authData" and append the result to "dst" func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte { if len(iv) != ivLen { log.Panicf("Only %d-byte IVs are supported", ivLen) @@ -114,7 +118,7 @@ func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte { return append(dst, buf...) } -// Open - decrypt "in" using "iv" and "authData" and append the result to "dst" +// Open decrypts "in" using "iv" and "authData" and append the result to "dst" func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) { if len(iv) != ivLen { log.Panicf("Only %d-byte IVs are supported", ivLen) diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 646dfc3..c9f6ee0 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -38,22 +38,27 @@ func EnospcPrealloc(fd int, off int64, len int64) (err error) { } } +// Fallocate wraps the Fallocate syscall. func Fallocate(fd int, mode uint32, off int64, len int64) (err error) { return syscall.Fallocate(fd, mode, off, len) } +// Openat wraps the Openat syscall. func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { return syscall.Openat(dirfd, path, flags, mode) } +// Renameat wraps the Renameat syscall. func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { return syscall.Renameat(olddirfd, oldpath, newdirfd, newpath) } +// Unlinkat wraps the Unlinkat syscall. func Unlinkat(dirfd int, path string) error { return syscall.Unlinkat(dirfd, path) } +// Mknodat wraps the Mknodat syscall. func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { return syscall.Mknodat(dirfd, path, mode, dev) } diff --git a/internal/tlog/log.go b/internal/tlog/log.go index a770df8..8db3de2 100644 --- a/internal/tlog/log.go +++ b/internal/tlog/log.go @@ -12,21 +12,34 @@ import ( ) const ( + // ProgramName is used in log reports. ProgramName = "gocryptfs" wpanicMsg = "-wpanic turns this warning into a panic: " ) // Escape sequences for terminal colors. These will be empty strings if stdout // is not a terminal. -var ColorReset, ColorGrey, ColorRed, ColorGreen, ColorYellow string +var ( + // ColorReset is used to reset terminal colors. + ColorReset string + // ColorGrey is a terminal color setting string. + ColorGrey string + // ColorRed is a terminal color setting string. + ColorRed string + // ColorGreen is a terminal color setting string. + ColorGreen string + // ColorYellow is a terminal color setting string. + ColorYellow string +) +// JSONDump writes the object in json form. func JSONDump(obj interface{}) string { b, err := json.MarshalIndent(obj, "", "\t") if err != nil { return err.Error() - } else { - return string(b) } + + return string(b) } // toggledLogger - a Logger than can be enabled and disabled diff --git a/main.go b/main.go index a22dd08..e5b2366 100644 --- a/main.go +++ b/main.go @@ -29,25 +29,25 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) +// Exit codes const ( - // Exit codes - ERREXIT_USAGE = 1 - ERREXIT_MOUNT = 3 - ERREXIT_CIPHERDIR = 6 - ERREXIT_INIT = 7 - ERREXIT_LOADCONF = 8 - ERREXIT_MOUNTPOINT = 10 + ErrExitUsage = 1 + ErrExitMount = 3 + ErrExitCipherDir = 6 + ErrExitInit = 7 + ErrExitLoadConf = 8 + ErrExitMountPoint = 10 ) const pleaseBuildBash = "[not set - please compile using ./build.bash]" -// gocryptfs version according to git, set by build.bash +// GitVersion is the gocryptfs version according to git, set by build.bash var GitVersion = pleaseBuildBash -// go-fuse library version, set by build.bash +// GitVersionFuse is the go-fuse library version, set by build.bash var GitVersionFuse = pleaseBuildBash -// Unix timestamp, set by build.bash +// BuildTime is the Unix timestamp, set by build.bash var BuildTime = "0" func usageText() { @@ -68,14 +68,14 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.Conf _, err := os.Stat(args.config) if err != nil { tlog.Fatal.Printf("Config file not found: %v", err) - os.Exit(ERREXIT_LOADCONF) + os.Exit(ErrExitLoadConf) } pw := readpassword.Once(args.extpass) tlog.Info.Println("Decrypting master key") masterkey, confFile, err = configfile.LoadConfFile(args.config, pw) if err != nil { tlog.Fatal.Println(err) - os.Exit(ERREXIT_LOADCONF) + os.Exit(ErrExitLoadConf) } return masterkey, confFile @@ -90,7 +90,7 @@ func changePassword(args *argContainer) { err := confFile.WriteFile() if err != nil { tlog.Fatal.Println(err) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } tlog.Info.Printf("Password changed.") os.Exit(0) @@ -145,11 +145,11 @@ func main() { err = checkDir(args.cipherdir) if err != nil { tlog.Fatal.Printf("Invalid cipherdir: %v", err) - os.Exit(ERREXIT_CIPHERDIR) + os.Exit(ErrExitCipherDir) } } else { usageText() - os.Exit(ERREXIT_USAGE) + os.Exit(ErrExitUsage) } // "-q" if args.quiet { @@ -164,7 +164,7 @@ func main() { args.config, err = filepath.Abs(args.config) if err != nil { tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } tlog.Info.Printf("Using config file at custom location %s", args.config) } else if args.reverse { @@ -179,7 +179,7 @@ func main() { f, err = os.Create(args.cpuprofile) if err != nil { tlog.Fatal.Println(err) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() @@ -191,7 +191,7 @@ func main() { f, err = os.Create(args.memprofile) if err != nil { tlog.Fatal.Println(err) - os.Exit(ERREXIT_INIT) + os.Exit(ErrExitInit) } defer func() { pprof.WriteHeapProfile(f) @@ -213,7 +213,7 @@ func main() { if args.init { if flagSet.NArg() > 1 { tlog.Fatal.Printf("Usage: %s -init [OPTIONS] CIPHERDIR", tlog.ProgramName) - os.Exit(ERREXIT_USAGE) + os.Exit(ErrExitUsage) } initDir(&args) // does not return } @@ -221,7 +221,7 @@ func main() { if args.passwd { if flagSet.NArg() > 1 { tlog.Fatal.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR", tlog.ProgramName) - os.Exit(ERREXIT_USAGE) + os.Exit(ErrExitUsage) } changePassword(&args) // does not return } @@ -229,17 +229,17 @@ func main() { // Check mountpoint if flagSet.NArg() != 2 { tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT", tlog.ProgramName) - os.Exit(ERREXIT_USAGE) + os.Exit(ErrExitUsage) } args.mountpoint, err = filepath.Abs(flagSet.Arg(1)) if err != nil { tlog.Fatal.Printf("Invalid mountpoint: %v", err) - os.Exit(ERREXIT_MOUNTPOINT) + os.Exit(ErrExitMountPoint) } err = checkDirEmpty(args.mountpoint) if err != nil { tlog.Fatal.Printf("Invalid mountpoint: %v", err) - os.Exit(ERREXIT_MOUNTPOINT) + os.Exit(ErrExitMountPoint) } // Get master key var masterkey []byte @@ -313,7 +313,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi frontendArgs.CryptoBackend = cryptocore.BackendAESSIV } else if args.reverse { tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file") - os.Exit(ERREXIT_USAGE) + os.Exit(ErrExitUsage) } } // If allow_other is set and we run as root, try to give newly created files to @@ -372,7 +372,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts) if err != nil { tlog.Fatal.Printf("Mount failed: %v", err) - os.Exit(ERREXIT_MOUNT) + os.Exit(ErrExitMount) } srv.SetDebug(args.fusedebug) diff --git a/tests/test_helpers/helpers.go b/tests/test_helpers/helpers.go index be532df..c38bbf2 100644 --- a/tests/test_helpers/helpers.go +++ b/tests/test_helpers/helpers.go @@ -17,16 +17,18 @@ import ( // TmpDir will be created inside this directory const testParentDir = "/tmp/gocryptfs-test-parent" + +// GocryptfsBinary is the assumed path to the gocryptfs build. const GocryptfsBinary = "../../gocryptfs" -// "go test" runs package tests in parallel! We create a unique TmpDir in -// init() so the tests do not interfere. +// TmpDir is a unique temporary directory. "go test" runs package tests in parallel. We create a +// unique TmpDir in init() so the tests do not interfere. var TmpDir string -// TmpDir + "/default-plain" +// DefaultPlainDir is TmpDir + "/default-plain" var DefaultPlainDir string -// TmpDir + "/default-cipher" +// DefaultCipherDir is TmpDir + "/default-cipher" var DefaultCipherDir string func init() { @@ -40,7 +42,7 @@ func init() { DefaultCipherDir = TmpDir + "/default-cipher" } -// ResetTmpDir - delete TmpDir, create new dir tree: +// ResetTmpDir deletes TmpDir, create new dir tree: // // TmpDir // |-- DefaultPlainDir @@ -167,7 +169,7 @@ func UnmountPanic(dir string) { } } -// UnmountError tries to unmount "dir" and returns the resulting error. +// UnmountErr tries to unmount "dir" and returns the resulting error. func UnmountErr(dir string) error { var cmd *exec.Cmd if runtime.GOOS == "darwin" { @@ -180,7 +182,7 @@ func UnmountErr(dir string) error { return cmd.Run() } -// Return md5 string for file "filename" +// Md5fn returns an md5 string for file "filename" func Md5fn(filename string) string { buf, err := ioutil.ReadFile(filename) if err != nil { @@ -190,14 +192,14 @@ func Md5fn(filename string) string { return Md5hex(buf) } -// Return md5 string for "buf" +// Md5hex returns an md5 string for "buf" func Md5hex(buf []byte) string { rawHash := md5.Sum(buf) hash := hex.EncodeToString(rawHash[:]) return hash } -// Verify that the file size equals "want". This checks: +// VerifySize checks that the file size equals "want". This checks: // 1) Size reported by Stat() // 2) Number of bytes returned when reading the whole file func VerifySize(t *testing.T, path string, want int) { @@ -216,7 +218,7 @@ func VerifySize(t *testing.T, path string, want int) { } } -// Create and delete a directory +// TestMkdirRmdir creates and deletes a directory func TestMkdirRmdir(t *testing.T, plainDir string) { dir := plainDir + "/dir1" err := os.Mkdir(dir, 0777) @@ -263,7 +265,7 @@ func TestMkdirRmdir(t *testing.T, plainDir string) { } } -// Create and rename a file +// TestRename creates and renames a file func TestRename(t *testing.T, plainDir string) { file1 := plainDir + "/rename1" file2 := plainDir + "/rename2" @@ -278,7 +280,7 @@ func TestRename(t *testing.T, plainDir string) { syscall.Unlink(file2) } -// verifyExistence - check in 3 ways that "path" exists: +// VerifyExistence checks in 3 ways that "path" exists: // stat, open, readdir func VerifyExistence(path string) bool { // Check that file can be stated