lint fixes
This commit is contained in:
parent
31a8f8b839
commit
b764917cd5
@ -71,7 +71,7 @@ func parseCliOpts() (args argContainer) {
|
|||||||
args.openssl, err = strconv.ParseBool(opensslAuto)
|
args.openssl, err = strconv.ParseBool(opensslAuto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err)
|
tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err)
|
||||||
os.Exit(ERREXIT_USAGE)
|
os.Exit(ErrExitUsage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IVLen = contentenc.DefaultIVBits / 8
|
ivLen = contentenc.DefaultIVBits / 8
|
||||||
blockSize = contentenc.DefaultBS + IVLen + cryptocore.AuthTagLen
|
blockSize = contentenc.DefaultBS + ivLen + cryptocore.AuthTagLen
|
||||||
)
|
)
|
||||||
|
|
||||||
func errExit(err error) {
|
func errExit(err error) {
|
||||||
@ -22,7 +22,7 @@ func errExit(err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func prettyPrintHeader(h *contentenc.FileHeader) {
|
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)
|
fmt.Printf("Header: Version: %d, Id: %s\n", h.Version, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,13 +38,13 @@ func main() {
|
|||||||
errExit(err)
|
errExit(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
headerBytes := make([]byte, contentenc.HEADER_LEN)
|
headerBytes := make([]byte, contentenc.HeaderLen)
|
||||||
n, err := fd.ReadAt(headerBytes, 0)
|
n, err := fd.ReadAt(headerBytes, 0)
|
||||||
if err == io.EOF && n == 0 {
|
if err == io.EOF && n == 0 {
|
||||||
fmt.Println("empty file")
|
fmt.Println("empty file")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
} else if err == io.EOF {
|
} 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)
|
os.Exit(1)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
errExit(err)
|
errExit(err)
|
||||||
@ -57,8 +57,8 @@ func main() {
|
|||||||
var i int64
|
var i int64
|
||||||
for i = 0; ; i++ {
|
for i = 0; ; i++ {
|
||||||
blockLen := int64(blockSize)
|
blockLen := int64(blockSize)
|
||||||
off := contentenc.HEADER_LEN + i*blockSize
|
off := contentenc.HeaderLen + i*blockSize
|
||||||
iv := make([]byte, IVLen)
|
iv := make([]byte, ivLen)
|
||||||
_, err := fd.ReadAt(iv, off)
|
_, err := fd.ReadAt(iv, off)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
@ -76,7 +76,7 @@ func main() {
|
|||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
errExit(err2)
|
errExit(err2)
|
||||||
}
|
}
|
||||||
blockLen = (fi.Size() - contentenc.HEADER_LEN) % blockSize
|
blockLen = (fi.Size() - contentenc.HeaderLen) % blockSize
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
errExit(err)
|
errExit(err)
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,13 @@ func initDir(args *argContainer) {
|
|||||||
_, err = os.Stat(args.config)
|
_, err = os.Stat(args.config)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tlog.Fatal.Printf("Config file %q already exists", args.config)
|
tlog.Fatal.Printf("Config file %q already exists", args.config)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ErrExitInit)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = checkDirEmpty(args.cipherdir)
|
err = checkDirEmpty(args.cipherdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Invalid cipherdir: %v", err)
|
tlog.Fatal.Printf("Invalid cipherdir: %v", err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ErrExitInit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Choose password for config file
|
// 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)
|
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ErrExitInit)
|
||||||
}
|
}
|
||||||
// Forward mode with filename encryption enabled needs a gocryptfs.diriv
|
// Forward mode with filename encryption enabled needs a gocryptfs.diriv
|
||||||
// in the root dir
|
// in the root dir
|
||||||
@ -48,7 +48,7 @@ func initDir(args *argContainer) {
|
|||||||
err = nametransform.WriteDirIV(args.cipherdir)
|
err = nametransform.WriteDirIV(args.cipherdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ErrExitInit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mountArgs := ""
|
mountArgs := ""
|
||||||
|
@ -14,31 +14,35 @@ import (
|
|||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// ConfDefaultName is the default configuration file name.
|
||||||
// The dot "." is not used in base64url (RFC4648), hence
|
// The dot "." is not used in base64url (RFC4648), hence
|
||||||
// we can never clash with an encrypted file.
|
// we can never clash with an encrypted file.
|
||||||
ConfDefaultName = "gocryptfs.conf"
|
ConfDefaultName = "gocryptfs.conf"
|
||||||
// In reverse mode, the config file gets stored next to the plain-text
|
// ConfReverseName is the default configuration file name in reverse mode,
|
||||||
// files. Make it hidden (start with dot) to not annoy the user.
|
// 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"
|
ConfReverseName = ".gocryptfs.reverse.conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ConfFile is the content of a config file.
|
||||||
type ConfFile struct {
|
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
|
// This only documents the config file for humans who look at it. The actual
|
||||||
// technical info is contained in FeatureFlags.
|
// technical info is contained in FeatureFlags.
|
||||||
Creator string
|
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
|
EncryptedKey []byte
|
||||||
// Stores parameters for scrypt hashing (key derivation)
|
// ScryptObject stores parameters for scrypt hashing (key derivation)
|
||||||
ScryptObject scryptKdf
|
ScryptObject ScryptKDF
|
||||||
// The On-Disk-Format version this filesystem uses
|
// Version is the On-Disk-Format version this filesystem uses
|
||||||
Version uint16
|
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
|
// 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
|
// mounting. This mechanism is analogous to the ext4 feature flags that are
|
||||||
// stored in the superblock.
|
// stored in the superblock.
|
||||||
FeatureFlags []string
|
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
|
filename string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +166,7 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
|
|||||||
// cf.ScryptObject.
|
// cf.ScryptObject.
|
||||||
func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
|
func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
|
||||||
// Generate derived key from password
|
// Generate derived key from password
|
||||||
cf.ScryptObject = NewScryptKdf(logN)
|
cf.ScryptObject = NewScryptKDF(logN)
|
||||||
scryptHash := cf.ScryptObject.DeriveKey(password)
|
scryptHash := cf.ScryptObject.DeriveKey(password)
|
||||||
|
|
||||||
// Lock master key using password-based key
|
// Lock master key using password-based key
|
||||||
|
@ -3,16 +3,24 @@ package configfile
|
|||||||
type flagIota int
|
type flagIota int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// FlagPlaintextNames indicates that filenames are unencrypted.
|
||||||
FlagPlaintextNames flagIota = iota
|
FlagPlaintextNames flagIota = iota
|
||||||
|
// FlagDirIV indicates that a per-directory IV file is used.
|
||||||
FlagDirIV
|
FlagDirIV
|
||||||
|
// FlagEMENames indicates EME (ECB-Mix-ECB) filename encryption.
|
||||||
|
// This flag is mandatory since gocryptfs v1.0.
|
||||||
FlagEMENames
|
FlagEMENames
|
||||||
|
// FlagGCMIV128 indicates 128-bit GCM IVs.
|
||||||
|
// This flag is mandatory since gocryptfs v1.0.
|
||||||
FlagGCMIV128
|
FlagGCMIV128
|
||||||
|
// FlagLongNames allows file names longer than 176 bytes.
|
||||||
FlagLongNames
|
FlagLongNames
|
||||||
|
// FlagAESSIV selects an AES-SIV based crypto backend.
|
||||||
FlagAESSIV
|
FlagAESSIV
|
||||||
)
|
)
|
||||||
|
|
||||||
// knownFlags stores the known feature flags and their string representation
|
// 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",
|
FlagPlaintextNames: "PlaintextNames",
|
||||||
FlagDirIV: "DirIV",
|
FlagDirIV: "DirIV",
|
||||||
FlagEMENames: "EMENames",
|
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.
|
// Filesystems that do not have these feature flags set are deprecated.
|
||||||
var requiredFlagsNormal []flagIota = []flagIota{
|
var requiredFlagsNormal = []flagIota{
|
||||||
FlagDirIV,
|
FlagDirIV,
|
||||||
FlagEMENames,
|
FlagEMENames,
|
||||||
FlagGCMIV128,
|
FlagGCMIV128,
|
||||||
@ -30,11 +38,11 @@ var requiredFlagsNormal []flagIota = []flagIota{
|
|||||||
|
|
||||||
// Filesystems without filename encryption obviously don't have or need the
|
// Filesystems without filename encryption obviously don't have or need the
|
||||||
// filename related feature flags.
|
// filename related feature flags.
|
||||||
var requiredFlagsPlaintextNames []flagIota = []flagIota{
|
var requiredFlagsPlaintextNames = []flagIota{
|
||||||
FlagGCMIV128,
|
FlagGCMIV128,
|
||||||
}
|
}
|
||||||
|
|
||||||
// isFeatureFlagKnown verifies that we understand a feature flag
|
// isFeatureFlagKnown verifies that we understand a feature flag.
|
||||||
func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
|
func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
|
||||||
for _, knownFlag := range knownFlags {
|
for _, knownFlag := range knownFlags {
|
||||||
if knownFlag == flag {
|
if knownFlag == flag {
|
||||||
@ -44,7 +52,7 @@ func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
|
|||||||
return false
|
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 {
|
func (cf *ConfFile) IsFeatureFlagSet(flagWant flagIota) bool {
|
||||||
flagString := knownFlags[flagWant]
|
flagString := knownFlags[flagWant]
|
||||||
for _, flag := range cf.FeatureFlags {
|
for _, flag := range cf.FeatureFlags {
|
||||||
|
@ -12,12 +12,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// ScryptDefaultLogN is the default scrypt logN configuration parameter.
|
||||||
// 1 << 16 uses 64MB of memory,
|
// 1 << 16 uses 64MB of memory,
|
||||||
// takes 4 seconds on my Atom Z3735F netbook
|
// takes 4 seconds on my Atom Z3735F netbook
|
||||||
ScryptDefaultLogN = 16
|
ScryptDefaultLogN = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
type scryptKdf struct {
|
// ScryptKDF is an instance of the scrypt key deriviation function.
|
||||||
|
type ScryptKDF struct {
|
||||||
Salt []byte
|
Salt []byte
|
||||||
N int
|
N int
|
||||||
R int
|
R int
|
||||||
@ -25,8 +27,9 @@ type scryptKdf struct {
|
|||||||
KeyLen int
|
KeyLen int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewScryptKdf(logN int) scryptKdf {
|
// NewScryptKDF returns a new instance of ScryptKDF.
|
||||||
var s scryptKdf
|
func NewScryptKDF(logN int) ScryptKDF {
|
||||||
|
var s ScryptKDF
|
||||||
s.Salt = cryptocore.RandBytes(cryptocore.KeyLen)
|
s.Salt = cryptocore.RandBytes(cryptocore.KeyLen)
|
||||||
if logN <= 0 {
|
if logN <= 0 {
|
||||||
s.N = 1 << ScryptDefaultLogN
|
s.N = 1 << ScryptDefaultLogN
|
||||||
@ -43,7 +46,8 @@ func NewScryptKdf(logN int) scryptKdf {
|
|||||||
return s
|
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)
|
k, err := scrypt.Key([]byte(pw), s.Salt, s.N, s.R, s.P, s.KeyLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("DeriveKey failed: %v", err)
|
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.
|
// LogN - N is saved as 2^LogN, but LogN is much easier to work with.
|
||||||
// This function gives you LogN = Log2(N).
|
// 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)
|
return int(math.Log2(float64(s.N)) + 0.5)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ ok github.com/rfjakob/gocryptfs/cryptfs 18.772s
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func benchmarkScryptN(n int, b *testing.B) {
|
func benchmarkScryptN(n int, b *testing.B) {
|
||||||
kdf := NewScryptKdf(n)
|
kdf := NewScryptKDF(n)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
kdf.DeriveKey("test")
|
kdf.DeriveKey("test")
|
||||||
}
|
}
|
||||||
|
@ -11,21 +11,28 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NonceMode determines how nonces are created.
|
||||||
type NonceMode int
|
type NonceMode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Default plaintext block size
|
// DefaultBS is the default plaintext block size
|
||||||
DefaultBS = 4096
|
DefaultBS = 4096
|
||||||
|
// DefaultIVBits is the default length of IV, in bits.
|
||||||
// We always use 128-bit IVs for file content, but the
|
// We always use 128-bit IVs for file content, but the
|
||||||
// key in the config file is encrypted with a 96-bit IV.
|
// key in the config file is encrypted with a 96-bit IV.
|
||||||
DefaultIVBits = 128
|
DefaultIVBits = 128
|
||||||
|
|
||||||
_ = iota // skip zero
|
_ = iota // skip zero
|
||||||
RandomNonce NonceMode = iota
|
// RandomNonce chooses a random nonce.
|
||||||
|
RandomNonce NonceMode = iota
|
||||||
|
// ReverseDeterministicNonce chooses a deterministic nonce, suitable for
|
||||||
|
// use in reverse mode.
|
||||||
ReverseDeterministicNonce NonceMode = iota
|
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 {
|
type ContentEnc struct {
|
||||||
// Cryptographic primitives
|
// Cryptographic primitives
|
||||||
cryptoCore *cryptocore.CryptoCore
|
cryptoCore *cryptocore.CryptoCore
|
||||||
@ -39,8 +46,8 @@ type ContentEnc struct {
|
|||||||
allZeroNonce []byte
|
allZeroNonce []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New returns an initialized ContentEnc instance.
|
||||||
func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc {
|
func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc {
|
||||||
|
|
||||||
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
|
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
|
||||||
|
|
||||||
return &ContentEnc{
|
return &ContentEnc{
|
||||||
@ -62,16 +69,16 @@ func (be *ContentEnc) CipherBS() uint64 {
|
|||||||
return be.cipherBS
|
return be.cipherBS
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptBlocks - Decrypt a number of blocks
|
// DecryptBlocks decrypts a number of blocks
|
||||||
// TODO refactor to three-param for
|
// 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)
|
cBuf := bytes.NewBuffer(ciphertext)
|
||||||
var err error
|
var err error
|
||||||
var pBuf bytes.Buffer
|
var pBuf bytes.Buffer
|
||||||
for cBuf.Len() > 0 {
|
for cBuf.Len() > 0 {
|
||||||
cBlock := cBuf.Next(int(be.cipherBS))
|
cBlock := cBuf.Next(int(be.cipherBS))
|
||||||
var pBlock []byte
|
var pBlock []byte
|
||||||
pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileId)
|
pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
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
|
// Corner case: A full-sized block of all-zero ciphertext bytes is translated
|
||||||
// to an all-zero plaintext block, i.e. file hole passtrough.
|
// 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?
|
// Empty block?
|
||||||
if len(ciphertext) == 0 {
|
if len(ciphertext) == 0 {
|
||||||
@ -114,7 +121,7 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b
|
|||||||
// Decrypt
|
// Decrypt
|
||||||
var plaintext []byte
|
var plaintext []byte
|
||||||
aData := make([]byte, 8)
|
aData := make([]byte, 8)
|
||||||
aData = append(aData, fileId...)
|
aData = append(aData, fileID...)
|
||||||
binary.BigEndian.PutUint64(aData, blockNo)
|
binary.BigEndian.PutUint64(aData, blockNo)
|
||||||
plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData)
|
plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData)
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ func TestCiphertextRange(t *testing.T) {
|
|||||||
if alignedLength < r.length {
|
if alignedLength < r.length {
|
||||||
t.Errorf("alignedLength=%d is smaller than length=%d", 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)
|
t.Errorf("alignedOffset=%d is not aligned", alignedOffset)
|
||||||
}
|
}
|
||||||
if r.offset%f.plainBS != 0 && skipBytes == 0 {
|
if r.offset%f.plainBS != 0 && skipBytes == 0 {
|
||||||
@ -81,7 +81,7 @@ func TestBlockNo(t *testing.T) {
|
|||||||
if b != 0 {
|
if b != 0 {
|
||||||
t.Errorf("actual: %d", b)
|
t.Errorf("actual: %d", b)
|
||||||
}
|
}
|
||||||
b = f.CipherOffToBlockNo(HEADER_LEN + f.cipherBS)
|
b = f.CipherOffToBlockNo(HeaderLen + f.cipherBS)
|
||||||
if b != 1 {
|
if b != 1 {
|
||||||
t.Errorf("actual: %d", b)
|
t.Errorf("actual: %d", b)
|
||||||
}
|
}
|
||||||
|
@ -12,42 +12,44 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Current On-Disk-Format version
|
// CurrentVersion is the current On-Disk-Format version
|
||||||
CurrentVersion = 2
|
CurrentVersion = 2
|
||||||
|
|
||||||
HEADER_VERSION_LEN = 2 // uint16
|
headerVersionLen = 2 // uint16
|
||||||
HEADER_ID_LEN = 16 // 128 bit random file id
|
headerIDLen = 16 // 128 bit random file id
|
||||||
HEADER_LEN = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length
|
// HeaderLen is the total header length
|
||||||
|
HeaderLen = headerVersionLen + headerIDLen
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FileHeader represents the header stored on each non-empty file.
|
||||||
type FileHeader struct {
|
type FileHeader struct {
|
||||||
Version uint16
|
Version uint16
|
||||||
Id []byte
|
ID []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack - serialize fileHeader object
|
// Pack - serialize fileHeader object
|
||||||
func (h *FileHeader) Pack() []byte {
|
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")
|
panic("FileHeader object not properly initialized")
|
||||||
}
|
}
|
||||||
buf := make([]byte, HEADER_LEN)
|
buf := make([]byte, HeaderLen)
|
||||||
binary.BigEndian.PutUint16(buf[0:HEADER_VERSION_LEN], h.Version)
|
binary.BigEndian.PutUint16(buf[0:headerVersionLen], h.Version)
|
||||||
copy(buf[HEADER_VERSION_LEN:], h.Id)
|
copy(buf[headerVersionLen:], h.ID)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseHeader - parse "buf" into fileHeader object
|
// ParseHeader - parse "buf" into fileHeader object
|
||||||
func ParseHeader(buf []byte) (*FileHeader, error) {
|
func ParseHeader(buf []byte) (*FileHeader, error) {
|
||||||
if len(buf) != HEADER_LEN {
|
if len(buf) != HeaderLen {
|
||||||
return nil, fmt.Errorf("ParseHeader: invalid length: got %d, want %d", len(buf), HEADER_LEN)
|
return nil, fmt.Errorf("ParseHeader: invalid length: got %d, want %d", len(buf), HeaderLen)
|
||||||
}
|
}
|
||||||
var h FileHeader
|
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 {
|
if h.Version != CurrentVersion {
|
||||||
return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", 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
|
return &h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +57,6 @@ func ParseHeader(buf []byte) (*FileHeader, error) {
|
|||||||
func RandomHeader() *FileHeader {
|
func RandomHeader() *FileHeader {
|
||||||
var h FileHeader
|
var h FileHeader
|
||||||
h.Version = CurrentVersion
|
h.Version = CurrentVersion
|
||||||
h.Id = cryptocore.RandBytes(HEADER_ID_LEN)
|
h.ID = cryptocore.RandBytes(headerIDLen)
|
||||||
return &h
|
return &h
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package contentenc
|
package contentenc
|
||||||
|
|
||||||
// intraBlock identifies a part of a file block
|
// IntraBlock identifies a part of a file block
|
||||||
type intraBlock struct {
|
type IntraBlock struct {
|
||||||
// Block number in the file
|
// BlockNo is the block number in the file
|
||||||
BlockNo uint64
|
BlockNo uint64
|
||||||
// Offset into block payload
|
// Skip is an offset into the block payload
|
||||||
// In forwared mode: block plaintext
|
// In forwared mode: block plaintext
|
||||||
// In reverse mode: offset into block ciphertext. Takes the header into
|
// In reverse mode: offset into block ciphertext. Takes the header into
|
||||||
// account.
|
// account.
|
||||||
@ -17,8 +17,8 @@ type intraBlock struct {
|
|||||||
fs *ContentEnc
|
fs *ContentEnc
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPartial - is the block partial? This means we have to do read-modify-write.
|
// IsPartial - is the block partial? This means we have to do read-modify-write.
|
||||||
func (ib *intraBlock) IsPartial() bool {
|
func (ib *IntraBlock) IsPartial() bool {
|
||||||
if ib.Skip > 0 || ib.Length < ib.fs.plainBS {
|
if ib.Skip > 0 || ib.Length < ib.fs.plainBS {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -26,17 +26,17 @@ func (ib *intraBlock) IsPartial() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BlockCipherOff returns the ciphertext offset corresponding to BlockNo
|
// 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)
|
return ib.fs.BlockNoToCipherOff(ib.BlockNo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockPlainOff returns the plaintext offset corresponding to 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)
|
return ib.fs.BlockNoToPlainOff(ib.BlockNo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CropBlock - crop a potentially larger plaintext block down to the relevant part
|
// 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)
|
lenHave := len(d)
|
||||||
lenWant := int(ib.Skip + ib.Length)
|
lenWant := int(ib.Skip + ib.Length)
|
||||||
if lenHave < lenWant {
|
if lenHave < lenWant {
|
||||||
@ -45,8 +45,9 @@ func (ib *intraBlock) CropBlock(d []byte) []byte {
|
|||||||
return d[ib.Skip:lenWant]
|
return d[ib.Skip:lenWant]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ciphertext range corresponding to the sum of all "blocks" (complete blocks)
|
// JointCiphertextRange is the ciphertext range corresponding to the sum of all
|
||||||
func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64, length uint64) {
|
// "blocks" (complete blocks)
|
||||||
|
func (ib *IntraBlock) JointCiphertextRange(blocks []IntraBlock) (offset uint64, length uint64) {
|
||||||
firstBlock := blocks[0]
|
firstBlock := blocks[0]
|
||||||
lastBlock := blocks[len(blocks)-1]
|
lastBlock := blocks[len(blocks)-1]
|
||||||
|
|
||||||
@ -57,8 +58,9 @@ func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64,
|
|||||||
return offset, length
|
return offset, length
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plaintext range corresponding to the sum of all "blocks" (complete blocks)
|
// JointPlaintextRange is the plaintext range corresponding to the sum of all
|
||||||
func JointPlaintextRange(blocks []intraBlock) (offset uint64, length uint64) {
|
// "blocks" (complete blocks)
|
||||||
|
func JointPlaintextRange(blocks []IntraBlock) (offset uint64, length uint64) {
|
||||||
firstBlock := blocks[0]
|
firstBlock := blocks[0]
|
||||||
lastBlock := blocks[len(blocks)-1]
|
lastBlock := blocks[len(blocks)-1]
|
||||||
|
|
||||||
|
@ -8,43 +8,43 @@ import (
|
|||||||
|
|
||||||
// Contentenc methods that translate offsets between ciphertext and plaintext
|
// 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 {
|
func (be *ContentEnc) PlainOffToBlockNo(plainOffset uint64) uint64 {
|
||||||
return plainOffset / be.plainBS
|
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 {
|
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)
|
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 {
|
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 {
|
func (be *ContentEnc) BlockNoToPlainOff(blockNo uint64) uint64 {
|
||||||
return blockNo * be.plainBS
|
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 {
|
func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 {
|
||||||
// Zero-sized files stay zero-sized
|
// Zero-sized files stay zero-sized
|
||||||
if cipherSize == 0 {
|
if cipherSize == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if cipherSize == HEADER_LEN {
|
if cipherSize == HeaderLen {
|
||||||
tlog.Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize)
|
tlog.Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if cipherSize < HEADER_LEN {
|
if cipherSize < HeaderLen {
|
||||||
tlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HEADER_LEN)
|
tlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HeaderLen)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +52,12 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 {
|
|||||||
blockNo := be.CipherOffToBlockNo(cipherSize - 1)
|
blockNo := be.CipherOffToBlockNo(cipherSize - 1)
|
||||||
blockCount := blockNo + 1
|
blockCount := blockNo + 1
|
||||||
|
|
||||||
overhead := be.BlockOverhead()*blockCount + HEADER_LEN
|
overhead := be.BlockOverhead()*blockCount + HeaderLen
|
||||||
|
|
||||||
return cipherSize - overhead
|
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 {
|
func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 {
|
||||||
// Zero-sized files stay zero-sized
|
// Zero-sized files stay zero-sized
|
||||||
if plainSize == 0 {
|
if plainSize == 0 {
|
||||||
@ -68,16 +68,16 @@ func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 {
|
|||||||
blockNo := be.PlainOffToBlockNo(plainSize - 1)
|
blockNo := be.PlainOffToBlockNo(plainSize - 1)
|
||||||
blockCount := blockNo + 1
|
blockCount := blockNo + 1
|
||||||
|
|
||||||
overhead := be.BlockOverhead()*blockCount + HEADER_LEN
|
overhead := be.BlockOverhead()*blockCount + HeaderLen
|
||||||
|
|
||||||
return plainSize + overhead
|
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.
|
// Returns an empty slice if length == 0.
|
||||||
func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlock {
|
func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []IntraBlock {
|
||||||
var blocks []intraBlock
|
var blocks []IntraBlock
|
||||||
var nextBlock intraBlock
|
var nextBlock IntraBlock
|
||||||
nextBlock.fs = be
|
nextBlock.fs = be
|
||||||
|
|
||||||
for length > 0 {
|
for length > 0 {
|
||||||
@ -94,11 +94,11 @@ func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlo
|
|||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split a ciphertext byte range into (possibly partial) blocks
|
// ExplodeCipherRange splits a ciphertext byte range into (possibly partial)
|
||||||
// This is used in reverse mode when reading files
|
// blocks This is used in reverse mode when reading files
|
||||||
func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []intraBlock {
|
func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []IntraBlock {
|
||||||
var blocks []intraBlock
|
var blocks []IntraBlock
|
||||||
var nextBlock intraBlock
|
var nextBlock IntraBlock
|
||||||
nextBlock.fs = be
|
nextBlock.fs = be
|
||||||
|
|
||||||
for length > 0 {
|
for length > 0 {
|
||||||
@ -120,10 +120,12 @@ func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []intraBl
|
|||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockOverhead returns the per-block overhead.
|
||||||
func (be *ContentEnc) BlockOverhead() uint64 {
|
func (be *ContentEnc) BlockOverhead() uint64 {
|
||||||
return be.cipherBS - be.plainBS
|
return be.cipherBS - be.plainBS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MinUint64 returns the minimum of two uint64 values.
|
||||||
func MinUint64(x uint64, y uint64) uint64 {
|
func MinUint64(x uint64, y uint64) uint64 {
|
||||||
if x < y {
|
if x < y {
|
||||||
return x
|
return x
|
||||||
|
@ -12,18 +12,25 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BackendTypeEnum indicates the type of backend in use.
|
||||||
type BackendTypeEnum int
|
type BackendTypeEnum int
|
||||||
|
|
||||||
const (
|
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
|
AuthTagLen = 16
|
||||||
|
|
||||||
_ = iota // Skip zero
|
_ = iota // Skip zero
|
||||||
|
// BackendOpenSSL specifies the OpenSSL backend.
|
||||||
BackendOpenSSL BackendTypeEnum = iota
|
BackendOpenSSL BackendTypeEnum = iota
|
||||||
BackendGoGCM BackendTypeEnum = iota
|
// BackendGoGCM specifies the Go based GCM backend.
|
||||||
BackendAESSIV BackendTypeEnum = iota
|
BackendGoGCM BackendTypeEnum = iota
|
||||||
|
// BackendAESSIV specifies an AESSIV backend.
|
||||||
|
BackendAESSIV BackendTypeEnum = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CryptoCore is the low level crypto implementation.
|
||||||
type CryptoCore struct {
|
type CryptoCore struct {
|
||||||
// AES-256 block cipher. This is used for EME filename encryption.
|
// AES-256 block cipher. This is used for EME filename encryption.
|
||||||
BlockCipher cipher.Block
|
BlockCipher cipher.Block
|
||||||
@ -36,7 +43,7 @@ type CryptoCore struct {
|
|||||||
IVLen int
|
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
|
// 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
|
// support 96-bit IVs here because they are used for encrypting the master
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"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 {
|
func RandBytes(n int) []byte {
|
||||||
b := make([]byte, n)
|
b := make([]byte, n)
|
||||||
_, err := rand.Read(b)
|
_, err := rand.Read(b)
|
||||||
@ -20,7 +20,7 @@ func RandBytes(n int) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a secure random uint64
|
// RandUint64 returns a secure random uint64
|
||||||
func RandUint64() uint64 {
|
func RandUint64() uint64 {
|
||||||
b := RandBytes(8)
|
b := RandBytes(8)
|
||||||
return binary.BigEndian.Uint64(b)
|
return binary.BigEndian.Uint64(b)
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
"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 {
|
type Args struct {
|
||||||
Masterkey []byte
|
Masterkey []byte
|
||||||
Cipherdir string
|
Cipherdir string
|
||||||
|
@ -45,6 +45,7 @@ type file struct {
|
|||||||
loopbackFile nodefs.File
|
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) {
|
func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (nodefs.File, fuse.Status) {
|
||||||
var st syscall.Stat_t
|
var st syscall.Stat_t
|
||||||
err := syscall.Fstat(int(fd.Fd()), &st)
|
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
|
// Returns io.EOF if the file is empty
|
||||||
func (f *file) readHeader() error {
|
func (f *file) readHeader() error {
|
||||||
buf := make([]byte, contentenc.HEADER_LEN)
|
buf := make([]byte, contentenc.HeaderLen)
|
||||||
_, err := f.fd.ReadAt(buf, 0)
|
_, err := f.fd.ReadAt(buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -100,7 +101,7 @@ func (f *file) createHeader() error {
|
|||||||
buf := h.Pack()
|
buf := h.Pack()
|
||||||
|
|
||||||
// Prevent partially written (=corrupt) header by preallocating the space beforehand
|
// 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 {
|
if err != nil {
|
||||||
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error())
|
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error())
|
||||||
return err
|
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)
|
tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)
|
||||||
|
|
||||||
// Decrypt it
|
// 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 {
|
if err != nil {
|
||||||
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
|
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
|
||||||
cipherOff := f.contentEnc.BlockNoToCipherOff(curruptBlockNo)
|
cipherOff := f.contentEnc.BlockNoToCipherOff(curruptBlockNo)
|
||||||
@ -256,7 +257,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
|
|
||||||
// Encrypt
|
// Encrypt
|
||||||
blockOffset := b.BlockCipherOff()
|
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",
|
tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",
|
||||||
f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo)
|
f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo)
|
||||||
|
|
||||||
|
@ -14,7 +14,10 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FALLOC_DEFAULT is a "normal" fallocate operation
|
||||||
const FALLOC_DEFAULT = 0x00
|
const FALLOC_DEFAULT = 0x00
|
||||||
|
|
||||||
|
// FALLOC_FL_KEEP_SIZE allocates disk space while not modifying the file size
|
||||||
const FALLOC_FL_KEEP_SIZE = 0x01
|
const FALLOC_FL_KEEP_SIZE = 0x01
|
||||||
|
|
||||||
// Only warn once
|
// Only warn once
|
||||||
@ -116,11 +119,12 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
oldSize, err := f.statPlainSize()
|
oldSize, err := f.statPlainSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
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
|
// File size stays the same - nothing to do
|
||||||
if newSize == oldSize {
|
if newSize == oldSize {
|
||||||
return fuse.OK
|
return fuse.OK
|
||||||
@ -128,34 +132,34 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
// File grows
|
// File grows
|
||||||
if newSize > oldSize {
|
if newSize > oldSize {
|
||||||
return f.truncateGrowFile(oldSize, newSize)
|
return f.truncateGrowFile(oldSize, newSize)
|
||||||
} else {
|
}
|
||||||
// File shrinks
|
|
||||||
blockNo := f.contentEnc.PlainOffToBlockNo(newSize)
|
// File shrinks
|
||||||
cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo)
|
blockNo := f.contentEnc.PlainOffToBlockNo(newSize)
|
||||||
plainOff := f.contentEnc.BlockNoToPlainOff(blockNo)
|
cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo)
|
||||||
lastBlockLen := newSize - plainOff
|
plainOff := f.contentEnc.BlockNoToPlainOff(blockNo)
|
||||||
var data []byte
|
lastBlockLen := newSize - plainOff
|
||||||
if lastBlockLen > 0 {
|
var data []byte
|
||||||
var status fuse.Status
|
if lastBlockLen > 0 {
|
||||||
data, status = f.doRead(plainOff, lastBlockLen)
|
var status fuse.Status
|
||||||
if status != fuse.OK {
|
data, status = f.doRead(plainOff, lastBlockLen)
|
||||||
tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err)
|
if status != fuse.OK {
|
||||||
return status
|
tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err)
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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 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
|
// 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()
|
off := lastBlock.BlockPlainOff()
|
||||||
_, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip))
|
_, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip))
|
||||||
return status
|
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)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FS implements the go-fuse virtual filesystem interface.
|
||||||
type FS struct {
|
type FS struct {
|
||||||
pathfs.FileSystem // loopbackFileSystem, see go-fuse/fuse/pathfs/loopback.go
|
pathfs.FileSystem // loopbackFileSystem, see go-fuse/fuse/pathfs/loopback.go
|
||||||
args Args // Stores configuration arguments
|
args Args // Stores configuration arguments
|
||||||
@ -35,7 +36,9 @@ type FS struct {
|
|||||||
contentEnc *contentenc.ContentEnc
|
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 {
|
func NewFS(args Args) *FS {
|
||||||
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
|
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
|
||||||
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
|
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) {
|
func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||||
tlog.Debug.Printf("FS.GetAttr('%s')", name)
|
tlog.Debug.Printf("FS.GetAttr('%s')", name)
|
||||||
if fs.isFiltered(name) {
|
if fs.isFiltered(name) {
|
||||||
@ -85,6 +89,7 @@ func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int, writeOnly bool) {
|
|||||||
return newFlags, writeOnly
|
return newFlags, writeOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open implements pathfs.Filesystem.
|
||||||
func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return nil, fuse.EPERM
|
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)
|
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) {
|
func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return nil, fuse.EPERM
|
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)
|
return NewFile(fd, writeOnly, fs.contentEnc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chmod implements pathfs.Filesystem.
|
||||||
func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
@ -170,6 +177,7 @@ func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.
|
|||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chown implements pathfs.Filesystem.
|
||||||
func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
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)))
|
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) {
|
func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
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)
|
return fs.FileSystem.Mknod(cPath, mode, dev, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Truncate implements pathfs.Filesystem.
|
||||||
// Support truncate(2) by opening the file and calling ftruncate(2)
|
// Support truncate(2) by opening the file and calling ftruncate(2)
|
||||||
// While the glibc "truncate" wrapper seems to always use ftruncate, fsstress from
|
// While the glibc "truncate" wrapper seems to always use ftruncate, fsstress from
|
||||||
// xfstests uses this a lot by calling "truncate64" directly.
|
// 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
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utimens implements pathfs.Filesystem.
|
||||||
func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
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)
|
return fs.FileSystem.Utimens(cPath, a, m, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatFs implements pathfs.Filesystem.
|
||||||
func (fs *FS) StatFs(path string) *fuse.StatfsOut {
|
func (fs *FS) StatFs(path string) *fuse.StatfsOut {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return nil
|
return nil
|
||||||
@ -252,6 +264,7 @@ func (fs *FS) StatFs(path string) *fuse.StatfsOut {
|
|||||||
return fs.FileSystem.StatFs(cPath)
|
return fs.FileSystem.StatFs(cPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Readlink implements pathfs.Filesystem.
|
||||||
func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status fuse.Status) {
|
func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status fuse.Status) {
|
||||||
cPath, err := fs.getBackingPath(path)
|
cPath, err := fs.getBackingPath(path)
|
||||||
if err != nil {
|
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
|
return string(target), fuse.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlink implements pathfs.Filesystem.
|
||||||
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
@ -312,6 +326,7 @@ func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
|
|||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Symlink implements pathfs.Filesystem.
|
||||||
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
|
||||||
tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
|
tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
|
||||||
if fs.isFiltered(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)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rename implements pathfs.Filesystem.
|
||||||
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(newPath) {
|
if fs.isFiltered(newPath) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
@ -437,6 +453,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
|||||||
return fuse.OK
|
return fuse.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link implements pathfs.Filesystem.
|
||||||
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(newPath) {
|
if fs.isFiltered(newPath) {
|
||||||
return fuse.EPERM
|
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))
|
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) {
|
func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(path) {
|
if fs.isFiltered(path) {
|
||||||
return fuse.EPERM
|
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))
|
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) {
|
func (fs *FS) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
|
||||||
return nil, fuse.ENOSYS
|
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 {
|
func (fs *FS) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
|
||||||
return fuse.ENOSYS
|
return fuse.ENOSYS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListXAttr implements pathfs.Filesystem.
|
||||||
func (fs *FS) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
|
func (fs *FS) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
|
||||||
return nil, fuse.ENOSYS
|
return nil, fuse.ENOSYS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveXAttr implements pathfs.Filesystem.
|
||||||
func (fs *FS) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
func (fs *FS) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
|
||||||
return fuse.ENOSYS
|
return fuse.ENOSYS
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ func (fs *FS) mkdirWithIv(cPath string, mode uint32) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mkdir implements pathfs.FileSystem
|
||||||
func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
if fs.isFiltered(newPath) {
|
if fs.isFiltered(newPath) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
@ -97,6 +98,7 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
|
|||||||
return fuse.OK
|
return fuse.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rmdir implements pathfs.FileSystem
|
||||||
func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
|
||||||
cPath, err := fs.getBackingPath(path)
|
cPath, err := fs.getBackingPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -215,6 +217,7 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
|
|||||||
return fuse.OK
|
return fuse.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenDir implements pathfs.FileSystem
|
||||||
func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
|
func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
|
||||||
tlog.Debug.Printf("OpenDir(%s)", dirName)
|
tlog.Debug.Printf("OpenDir(%s)", dirName)
|
||||||
cDirName, err := fs.encryptPath(dirName)
|
cDirName, err := fs.encryptPath(dirName)
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewInoGen() *inoGenT {
|
func newInoGen() *inoGenT {
|
||||||
var ino uint64 = 1
|
var ino uint64 = 1
|
||||||
return &inoGenT{&ino}
|
return &inoGenT{&ino}
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,9 @@ func (rfs *reverseFS) findLongnameParent(dir string, dirIV []byte, longname stri
|
|||||||
}
|
}
|
||||||
if hit == "" {
|
if hit == "" {
|
||||||
return "", syscall.ENOENT
|
return "", syscall.ENOENT
|
||||||
} else {
|
|
||||||
return hit, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return hit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) {
|
func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) {
|
||||||
|
@ -39,7 +39,7 @@ func (rfs *reverseFS) NewFile(relPath string, flags uint32) (nodefs.File, fuse.S
|
|||||||
id := derivePathIV(relPath, ivPurposeFileID)
|
id := derivePathIV(relPath, ivPurposeFileID)
|
||||||
header := contentenc.FileHeader{
|
header := contentenc.FileHeader{
|
||||||
Version: contentenc.CurrentVersion,
|
Version: contentenc.CurrentVersion,
|
||||||
Id: id,
|
ID: id,
|
||||||
}
|
}
|
||||||
return &reverseFile{
|
return &reverseFile{
|
||||||
File: nodefs.NewDefaultFile(),
|
File: nodefs.NewDefaultFile(),
|
||||||
@ -58,7 +58,7 @@ func (rf *reverseFile) GetAttr(*fuse.Attr) fuse.Status {
|
|||||||
|
|
||||||
// encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks.
|
// encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks.
|
||||||
// "plaintext" must already be block-aligned.
|
// "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))
|
nonce := make([]byte, len(block0IV))
|
||||||
copy(nonce, block0IV)
|
copy(nonce, block0IV)
|
||||||
block0IVlow := binary.BigEndian.Uint64(block0IV[8:])
|
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++ {
|
for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ {
|
||||||
binary.BigEndian.PutUint64(nonceLow, block0IVlow+blockNo)
|
binary.BigEndian.PutUint64(nonceLow, block0IVlow+blockNo)
|
||||||
inBlock := inBuf.Next(bs)
|
inBlock := inBuf.Next(bs)
|
||||||
outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileId, nonce)
|
outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileID, nonce)
|
||||||
outBuf.Write(outBlock)
|
outBuf.Write(outBlock)
|
||||||
}
|
}
|
||||||
return outBuf.Bytes()
|
return outBuf.Bytes()
|
||||||
@ -95,7 +95,7 @@ func (rf *reverseFile) readBackingFile(off uint64, length uint64) (out []byte, e
|
|||||||
plaintext = plaintext[0:n]
|
plaintext = plaintext[0:n]
|
||||||
|
|
||||||
// Encrypt blocks
|
// 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
|
// Crop down to the relevant part
|
||||||
lenHave := len(ciphertext)
|
lenHave := len(ciphertext)
|
||||||
@ -118,7 +118,7 @@ func (rf *reverseFile) Read(buf []byte, ioff int64) (resultData fuse.ReadResult,
|
|||||||
var header []byte
|
var header []byte
|
||||||
|
|
||||||
// Synthesize file header
|
// Synthesize file header
|
||||||
if off < contentenc.HEADER_LEN {
|
if off < contentenc.HeaderLen {
|
||||||
header = rf.header.Pack()
|
header = rf.header.Pack()
|
||||||
// Truncate to requested part
|
// Truncate to requested part
|
||||||
end := int(off) + len(buf)
|
end := int(off) + len(buf)
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// DirIVMode is the mode to use for Dir IV files.
|
||||||
DirIVMode = syscall.S_IFREG | 0400
|
DirIVMode = syscall.S_IFREG | 0400
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,8 +43,10 @@ type reverseFS struct {
|
|||||||
inoMapLock sync.Mutex
|
inoMapLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypted FUSE overlay filesystem
|
var _ pathfs.FileSystem = &reverseFS{}
|
||||||
func NewFS(args fusefrontend.Args) *reverseFS {
|
|
||||||
|
// NewFS returns an encrypted FUSE overlay filesystem
|
||||||
|
func NewFS(args fusefrontend.Args) pathfs.FileSystem {
|
||||||
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
|
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
|
||||||
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
|
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
|
||||||
nameTransform := nametransform.New(cryptoCore, args.LongNames)
|
nameTransform := nametransform.New(cryptoCore, args.LongNames)
|
||||||
@ -55,7 +58,7 @@ func NewFS(args fusefrontend.Args) *reverseFS {
|
|||||||
args: args,
|
args: args,
|
||||||
nameTransform: nameTransform,
|
nameTransform: nameTransform,
|
||||||
contentEnc: contentEnc,
|
contentEnc: contentEnc,
|
||||||
inoGen: NewInoGen(),
|
inoGen: newInoGen(),
|
||||||
inoMap: map[devIno]uint64{},
|
inoMap: map[devIno]uint64{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// identical to AES block size
|
// DirIVLen is identical to AES block size
|
||||||
DirIVLen = 16
|
DirIVLen = 16
|
||||||
// dirIV is stored in this file. Exported because we have to ignore this
|
// DirIVFilename is the filename used to store directory IV.
|
||||||
// name in directory listing.
|
// Exported because we have to ignore this name in directory listing.
|
||||||
DirIVFilename = "gocryptfs.diriv"
|
DirIVFilename = "gocryptfs.diriv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// LongNameSuffix is the suffix used for files with long names.
|
||||||
// Files with long names are stored in two files:
|
// Files with long names are stored in two files:
|
||||||
// gocryptfs.longname.[sha256] <--- File content, prefix = gocryptfs.longname.
|
// gocryptfs.longname.[sha256] <--- File content, prefix = gocryptfs.longname.
|
||||||
// gocryptfs.longname.[sha256].name <--- File name, suffix = .name
|
// gocryptfs.longname.[sha256].name <--- File name, suffix = .name
|
||||||
@ -31,12 +32,13 @@ func HashLongName(name string) string {
|
|||||||
|
|
||||||
// Values returned by IsLongName
|
// Values returned by IsLongName
|
||||||
const (
|
const (
|
||||||
// File that stores the file content.
|
// LongNameContent is the file that stores the file content.
|
||||||
// Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=
|
// Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=
|
||||||
LongNameContent = iota
|
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
|
// Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=.name
|
||||||
LongNameFilename = iota
|
LongNameFilename = iota
|
||||||
|
// LongNameNone is used when the file does not have a long name.
|
||||||
// Example: i1bpTaVLZq7sRNA9mL_2Ig==
|
// Example: i1bpTaVLZq7sRNA9mL_2Ig==
|
||||||
LongNameNone = iota
|
LongNameNone = iota
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Package namtransforms encrypts and decrypts filenames.
|
// Package nametransform encrypts and decrypts filenames.
|
||||||
package nametransform
|
package nametransform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -12,12 +12,14 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NameTransform is used to transform filenames.
|
||||||
type NameTransform struct {
|
type NameTransform struct {
|
||||||
cryptoCore *cryptocore.CryptoCore
|
cryptoCore *cryptocore.CryptoCore
|
||||||
longNames bool
|
longNames bool
|
||||||
DirIVCache dirIVCache
|
DirIVCache dirIVCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New returns a new NameTransform instance.
|
||||||
func New(c *cryptocore.CryptoCore, longNames bool) *NameTransform {
|
func New(c *cryptocore.CryptoCore, longNames bool) *NameTransform {
|
||||||
return &NameTransform{
|
return &NameTransform{
|
||||||
cryptoCore: c,
|
cryptoCore: c,
|
||||||
@ -51,7 +53,7 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error
|
|||||||
return plain, err
|
return plain, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// encryptName - encrypt "plainName", return base64-encoded "cipherName64".
|
// EncryptName encrypts "plainName", returns a base64-encoded "cipherName64".
|
||||||
// Used internally by EncryptPathDirIV().
|
// Used internally by EncryptPathDirIV().
|
||||||
// The encryption is either CBC or EME, depending on "useEME".
|
// The encryption is either CBC or EME, depending on "useEME".
|
||||||
//
|
//
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PreferOpenSSL returns true if OpenSSL should be used.
|
||||||
func PreferOpenSSL() bool {
|
func PreferOpenSSL() bool {
|
||||||
if stupidgcm.BuiltWithoutOpenssl {
|
if stupidgcm.BuiltWithoutOpenssl {
|
||||||
return false
|
return false
|
||||||
|
@ -17,8 +17,8 @@ const (
|
|||||||
exitCode = 9
|
exitCode = 9
|
||||||
)
|
)
|
||||||
|
|
||||||
// Once() tries to get a password from the user, either from the terminal,
|
// Once tries to get a password from the user, either from the terminal, extpass
|
||||||
// extpass or stdin.
|
// or stdin.
|
||||||
func Once(extpass string) string {
|
func Once(extpass string) string {
|
||||||
if extpass != "" {
|
if extpass != "" {
|
||||||
return readPasswordExtpass(extpass)
|
return readPasswordExtpass(extpass)
|
||||||
@ -29,8 +29,8 @@ func Once(extpass string) string {
|
|||||||
return readPasswordTerminal("Password: ")
|
return readPasswordTerminal("Password: ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Twice() is the same as Once but will prompt twice if we get
|
// Twice is the same as Once but will prompt twice if we get the password from
|
||||||
// the password from the terminal.
|
// the terminal.
|
||||||
func Twice(extpass string) string {
|
func Twice(extpass string) string {
|
||||||
if extpass != "" {
|
if extpass != "" {
|
||||||
return readPasswordExtpass(extpass)
|
return readPasswordExtpass(extpass)
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
package siv_aead
|
package siv_aead
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
|
||||||
"github.com/jacobsa/crypto/siv"
|
"github.com/jacobsa/crypto/siv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,7 +12,10 @@ type sivAead struct {
|
|||||||
key []byte
|
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{
|
return &sivAead{
|
||||||
key: key,
|
key: key,
|
||||||
}
|
}
|
||||||
@ -25,7 +30,7 @@ func (s *sivAead) Overhead() int {
|
|||||||
return 16
|
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 {
|
func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte {
|
||||||
if len(nonce) != 16 {
|
if len(nonce) != 16 {
|
||||||
// SIV supports any nonce size, but in gocryptfs we exclusively use 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
|
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) {
|
func (s *sivAead) Open(dst, nonce, ciphertext, authData []byte) ([]byte, error) {
|
||||||
if len(nonce) != 16 {
|
if len(nonce) != 16 {
|
||||||
// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
|
// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
|
||||||
|
@ -9,6 +9,7 @@ package stupidgcm
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -28,7 +29,10 @@ type stupidGCM struct {
|
|||||||
key []byte
|
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 {
|
if len(key) != keyLen {
|
||||||
log.Panicf("Only %d-byte keys are supported", keyLen)
|
log.Panicf("Only %d-byte keys are supported", keyLen)
|
||||||
}
|
}
|
||||||
@ -43,7 +47,7 @@ func (g stupidGCM) Overhead() int {
|
|||||||
return tagLen
|
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 {
|
func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte {
|
||||||
if len(iv) != ivLen {
|
if len(iv) != ivLen {
|
||||||
log.Panicf("Only %d-byte IVs are supported", 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...)
|
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) {
|
func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
|
||||||
if len(iv) != ivLen {
|
if len(iv) != ivLen {
|
||||||
log.Panicf("Only %d-byte IVs are supported", ivLen)
|
log.Panicf("Only %d-byte IVs are supported", ivLen)
|
||||||
|
@ -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) {
|
func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
|
||||||
return syscall.Fallocate(fd, mode, off, len)
|
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) {
|
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
|
||||||
return syscall.Openat(dirfd, path, flags, mode)
|
return syscall.Openat(dirfd, path, flags, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Renameat wraps the Renameat syscall.
|
||||||
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
|
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
|
||||||
return syscall.Renameat(olddirfd, oldpath, newdirfd, newpath)
|
return syscall.Renameat(olddirfd, oldpath, newdirfd, newpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlinkat wraps the Unlinkat syscall.
|
||||||
func Unlinkat(dirfd int, path string) error {
|
func Unlinkat(dirfd int, path string) error {
|
||||||
return syscall.Unlinkat(dirfd, path)
|
return syscall.Unlinkat(dirfd, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mknodat wraps the Mknodat syscall.
|
||||||
func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
|
func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
|
||||||
return syscall.Mknodat(dirfd, path, mode, dev)
|
return syscall.Mknodat(dirfd, path, mode, dev)
|
||||||
}
|
}
|
||||||
|
@ -12,21 +12,34 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// ProgramName is used in log reports.
|
||||||
ProgramName = "gocryptfs"
|
ProgramName = "gocryptfs"
|
||||||
wpanicMsg = "-wpanic turns this warning into a panic: "
|
wpanicMsg = "-wpanic turns this warning into a panic: "
|
||||||
)
|
)
|
||||||
|
|
||||||
// Escape sequences for terminal colors. These will be empty strings if stdout
|
// Escape sequences for terminal colors. These will be empty strings if stdout
|
||||||
// is not a terminal.
|
// 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 {
|
func JSONDump(obj interface{}) string {
|
||||||
b, err := json.MarshalIndent(obj, "", "\t")
|
b, err := json.MarshalIndent(obj, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
return err.Error()
|
||||||
} else {
|
|
||||||
return string(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggledLogger - a Logger than can be enabled and disabled
|
// toggledLogger - a Logger than can be enabled and disabled
|
||||||
|
50
main.go
50
main.go
@ -29,25 +29,25 @@ import (
|
|||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Exit codes
|
||||||
const (
|
const (
|
||||||
// Exit codes
|
ErrExitUsage = 1
|
||||||
ERREXIT_USAGE = 1
|
ErrExitMount = 3
|
||||||
ERREXIT_MOUNT = 3
|
ErrExitCipherDir = 6
|
||||||
ERREXIT_CIPHERDIR = 6
|
ErrExitInit = 7
|
||||||
ERREXIT_INIT = 7
|
ErrExitLoadConf = 8
|
||||||
ERREXIT_LOADCONF = 8
|
ErrExitMountPoint = 10
|
||||||
ERREXIT_MOUNTPOINT = 10
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const pleaseBuildBash = "[not set - please compile using ./build.bash]"
|
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
|
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
|
var GitVersionFuse = pleaseBuildBash
|
||||||
|
|
||||||
// Unix timestamp, set by build.bash
|
// BuildTime is the Unix timestamp, set by build.bash
|
||||||
var BuildTime = "0"
|
var BuildTime = "0"
|
||||||
|
|
||||||
func usageText() {
|
func usageText() {
|
||||||
@ -68,14 +68,14 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.Conf
|
|||||||
_, err := os.Stat(args.config)
|
_, err := os.Stat(args.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Config file not found: %v", err)
|
tlog.Fatal.Printf("Config file not found: %v", err)
|
||||||
os.Exit(ERREXIT_LOADCONF)
|
os.Exit(ErrExitLoadConf)
|
||||||
}
|
}
|
||||||
pw := readpassword.Once(args.extpass)
|
pw := readpassword.Once(args.extpass)
|
||||||
tlog.Info.Println("Decrypting master key")
|
tlog.Info.Println("Decrypting master key")
|
||||||
masterkey, confFile, err = configfile.LoadConfFile(args.config, pw)
|
masterkey, confFile, err = configfile.LoadConfFile(args.config, pw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(ERREXIT_LOADCONF)
|
os.Exit(ErrExitLoadConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
return masterkey, confFile
|
return masterkey, confFile
|
||||||
@ -90,7 +90,7 @@ func changePassword(args *argContainer) {
|
|||||||
err := confFile.WriteFile()
|
err := confFile.WriteFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ErrExitInit)
|
||||||
}
|
}
|
||||||
tlog.Info.Printf("Password changed.")
|
tlog.Info.Printf("Password changed.")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@ -145,11 +145,11 @@ func main() {
|
|||||||
err = checkDir(args.cipherdir)
|
err = checkDir(args.cipherdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Invalid cipherdir: %v", err)
|
tlog.Fatal.Printf("Invalid cipherdir: %v", err)
|
||||||
os.Exit(ERREXIT_CIPHERDIR)
|
os.Exit(ErrExitCipherDir)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
usageText()
|
usageText()
|
||||||
os.Exit(ERREXIT_USAGE)
|
os.Exit(ErrExitUsage)
|
||||||
}
|
}
|
||||||
// "-q"
|
// "-q"
|
||||||
if args.quiet {
|
if args.quiet {
|
||||||
@ -164,7 +164,7 @@ func main() {
|
|||||||
args.config, err = filepath.Abs(args.config)
|
args.config, err = filepath.Abs(args.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err)
|
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)
|
tlog.Info.Printf("Using config file at custom location %s", args.config)
|
||||||
} else if args.reverse {
|
} else if args.reverse {
|
||||||
@ -179,7 +179,7 @@ func main() {
|
|||||||
f, err = os.Create(args.cpuprofile)
|
f, err = os.Create(args.cpuprofile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ErrExitInit)
|
||||||
}
|
}
|
||||||
pprof.StartCPUProfile(f)
|
pprof.StartCPUProfile(f)
|
||||||
defer pprof.StopCPUProfile()
|
defer pprof.StopCPUProfile()
|
||||||
@ -191,7 +191,7 @@ func main() {
|
|||||||
f, err = os.Create(args.memprofile)
|
f, err = os.Create(args.memprofile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ErrExitInit)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
pprof.WriteHeapProfile(f)
|
pprof.WriteHeapProfile(f)
|
||||||
@ -213,7 +213,7 @@ func main() {
|
|||||||
if args.init {
|
if args.init {
|
||||||
if flagSet.NArg() > 1 {
|
if flagSet.NArg() > 1 {
|
||||||
tlog.Fatal.Printf("Usage: %s -init [OPTIONS] CIPHERDIR", tlog.ProgramName)
|
tlog.Fatal.Printf("Usage: %s -init [OPTIONS] CIPHERDIR", tlog.ProgramName)
|
||||||
os.Exit(ERREXIT_USAGE)
|
os.Exit(ErrExitUsage)
|
||||||
}
|
}
|
||||||
initDir(&args) // does not return
|
initDir(&args) // does not return
|
||||||
}
|
}
|
||||||
@ -221,7 +221,7 @@ func main() {
|
|||||||
if args.passwd {
|
if args.passwd {
|
||||||
if flagSet.NArg() > 1 {
|
if flagSet.NArg() > 1 {
|
||||||
tlog.Fatal.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR", tlog.ProgramName)
|
tlog.Fatal.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR", tlog.ProgramName)
|
||||||
os.Exit(ERREXIT_USAGE)
|
os.Exit(ErrExitUsage)
|
||||||
}
|
}
|
||||||
changePassword(&args) // does not return
|
changePassword(&args) // does not return
|
||||||
}
|
}
|
||||||
@ -229,17 +229,17 @@ func main() {
|
|||||||
// Check mountpoint
|
// Check mountpoint
|
||||||
if flagSet.NArg() != 2 {
|
if flagSet.NArg() != 2 {
|
||||||
tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT", tlog.ProgramName)
|
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))
|
args.mountpoint, err = filepath.Abs(flagSet.Arg(1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Invalid mountpoint: %v", err)
|
tlog.Fatal.Printf("Invalid mountpoint: %v", err)
|
||||||
os.Exit(ERREXIT_MOUNTPOINT)
|
os.Exit(ErrExitMountPoint)
|
||||||
}
|
}
|
||||||
err = checkDirEmpty(args.mountpoint)
|
err = checkDirEmpty(args.mountpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Invalid mountpoint: %v", err)
|
tlog.Fatal.Printf("Invalid mountpoint: %v", err)
|
||||||
os.Exit(ERREXIT_MOUNTPOINT)
|
os.Exit(ErrExitMountPoint)
|
||||||
}
|
}
|
||||||
// Get master key
|
// Get master key
|
||||||
var masterkey []byte
|
var masterkey []byte
|
||||||
@ -313,7 +313,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
|
|||||||
frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
|
frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
|
||||||
} else if args.reverse {
|
} else if args.reverse {
|
||||||
tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file")
|
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
|
// 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)
|
srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Printf("Mount failed: %v", err)
|
tlog.Fatal.Printf("Mount failed: %v", err)
|
||||||
os.Exit(ERREXIT_MOUNT)
|
os.Exit(ErrExitMount)
|
||||||
}
|
}
|
||||||
srv.SetDebug(args.fusedebug)
|
srv.SetDebug(args.fusedebug)
|
||||||
|
|
||||||
|
@ -17,16 +17,18 @@ import (
|
|||||||
|
|
||||||
// TmpDir will be created inside this directory
|
// TmpDir will be created inside this directory
|
||||||
const testParentDir = "/tmp/gocryptfs-test-parent"
|
const testParentDir = "/tmp/gocryptfs-test-parent"
|
||||||
|
|
||||||
|
// GocryptfsBinary is the assumed path to the gocryptfs build.
|
||||||
const GocryptfsBinary = "../../gocryptfs"
|
const GocryptfsBinary = "../../gocryptfs"
|
||||||
|
|
||||||
// "go test" runs package tests in parallel! We create a unique TmpDir in
|
// TmpDir is a unique temporary directory. "go test" runs package tests in parallel. We create a
|
||||||
// init() so the tests do not interfere.
|
// unique TmpDir in init() so the tests do not interfere.
|
||||||
var TmpDir string
|
var TmpDir string
|
||||||
|
|
||||||
// TmpDir + "/default-plain"
|
// DefaultPlainDir is TmpDir + "/default-plain"
|
||||||
var DefaultPlainDir string
|
var DefaultPlainDir string
|
||||||
|
|
||||||
// TmpDir + "/default-cipher"
|
// DefaultCipherDir is TmpDir + "/default-cipher"
|
||||||
var DefaultCipherDir string
|
var DefaultCipherDir string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -40,7 +42,7 @@ func init() {
|
|||||||
DefaultCipherDir = TmpDir + "/default-cipher"
|
DefaultCipherDir = TmpDir + "/default-cipher"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetTmpDir - delete TmpDir, create new dir tree:
|
// ResetTmpDir deletes TmpDir, create new dir tree:
|
||||||
//
|
//
|
||||||
// TmpDir
|
// TmpDir
|
||||||
// |-- DefaultPlainDir
|
// |-- 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 {
|
func UnmountErr(dir string) error {
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
@ -180,7 +182,7 @@ func UnmountErr(dir string) error {
|
|||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return md5 string for file "filename"
|
// Md5fn returns an md5 string for file "filename"
|
||||||
func Md5fn(filename string) string {
|
func Md5fn(filename string) string {
|
||||||
buf, err := ioutil.ReadFile(filename)
|
buf, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -190,14 +192,14 @@ func Md5fn(filename string) string {
|
|||||||
return Md5hex(buf)
|
return Md5hex(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return md5 string for "buf"
|
// Md5hex returns an md5 string for "buf"
|
||||||
func Md5hex(buf []byte) string {
|
func Md5hex(buf []byte) string {
|
||||||
rawHash := md5.Sum(buf)
|
rawHash := md5.Sum(buf)
|
||||||
hash := hex.EncodeToString(rawHash[:])
|
hash := hex.EncodeToString(rawHash[:])
|
||||||
return hash
|
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()
|
// 1) Size reported by Stat()
|
||||||
// 2) Number of bytes returned when reading the whole file
|
// 2) Number of bytes returned when reading the whole file
|
||||||
func VerifySize(t *testing.T, path string, want int) {
|
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) {
|
func TestMkdirRmdir(t *testing.T, plainDir string) {
|
||||||
dir := plainDir + "/dir1"
|
dir := plainDir + "/dir1"
|
||||||
err := os.Mkdir(dir, 0777)
|
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) {
|
func TestRename(t *testing.T, plainDir string) {
|
||||||
file1 := plainDir + "/rename1"
|
file1 := plainDir + "/rename1"
|
||||||
file2 := plainDir + "/rename2"
|
file2 := plainDir + "/rename2"
|
||||||
@ -278,7 +280,7 @@ func TestRename(t *testing.T, plainDir string) {
|
|||||||
syscall.Unlink(file2)
|
syscall.Unlink(file2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyExistence - check in 3 ways that "path" exists:
|
// VerifyExistence checks in 3 ways that "path" exists:
|
||||||
// stat, open, readdir
|
// stat, open, readdir
|
||||||
func VerifyExistence(path string) bool {
|
func VerifyExistence(path string) bool {
|
||||||
// Check that file can be stated
|
// Check that file can be stated
|
||||||
|
Loading…
Reference in New Issue
Block a user