diff options
Diffstat (limited to 'internal')
29 files changed, 264 insertions, 163 deletions
| 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 | 
