Major refactoring: Split up "cryptfs" into several internal packages
"git status" for reference: deleted: cryptfs/cryptfs.go deleted: cryptfs/names_core.go modified: integration_tests/cli_test.go modified: integration_tests/helpers.go renamed: cryptfs/config_file.go -> internal/configfile/config_file.go renamed: cryptfs/config_test.go -> internal/configfile/config_test.go renamed: cryptfs/config_test/.gitignore -> internal/configfile/config_test/.gitignore renamed: cryptfs/config_test/PlaintextNames.conf -> internal/configfile/config_test/PlaintextNames.conf renamed: cryptfs/config_test/StrangeFeature.conf -> internal/configfile/config_test/StrangeFeature.conf renamed: cryptfs/config_test/v1.conf -> internal/configfile/config_test/v1.conf renamed: cryptfs/config_test/v2.conf -> internal/configfile/config_test/v2.conf renamed: cryptfs/kdf.go -> internal/configfile/kdf.go renamed: cryptfs/kdf_test.go -> internal/configfile/kdf_test.go renamed: cryptfs/cryptfs_content.go -> internal/contentenc/content.go new file: internal/contentenc/content_api.go renamed: cryptfs/content_test.go -> internal/contentenc/content_test.go renamed: cryptfs/file_header.go -> internal/contentenc/file_header.go renamed: cryptfs/intrablock.go -> internal/contentenc/intrablock.go renamed: cryptfs/address_translation.go -> internal/contentenc/offsets.go new file: internal/cryptocore/crypto_api.go renamed: cryptfs/gcm_go1.4.go -> internal/cryptocore/gcm_go1.4.go renamed: cryptfs/gcm_go1.5.go -> internal/cryptocore/gcm_go1.5.go renamed: cryptfs/nonce.go -> internal/cryptocore/nonce.go renamed: cryptfs/openssl_aead.go -> internal/cryptocore/openssl_aead.go renamed: cryptfs/openssl_benchmark.bash -> internal/cryptocore/openssl_benchmark.bash renamed: cryptfs/openssl_test.go -> internal/cryptocore/openssl_test.go new file: internal/nametransform/name_api.go new file: internal/nametransform/names_core.go renamed: cryptfs/names_diriv.go -> internal/nametransform/names_diriv.go renamed: cryptfs/names_noiv.go -> internal/nametransform/names_noiv.go renamed: cryptfs/names_test.go -> internal/nametransform/names_test.go new file: internal/nametransform/pad16.go renamed: cryptfs/log.go -> internal/toggledlog/log.go renamed: cryptfs/log_go1.4.go -> internal/toggledlog/log_go1.4.go renamed: cryptfs/log_go1.5.go -> internal/toggledlog/log_go1.5.go modified: main.go modified: masterkey.go modified: pathfs_frontend/file.go modified: pathfs_frontend/file_holes.go modified: pathfs_frontend/fs.go modified: pathfs_frontend/fs_dir.go modified: pathfs_frontend/names.go modified: test.bash
This commit is contained in:
parent
adcfbd79a8
commit
2b8cbd9441
@ -1,83 +0,0 @@
|
|||||||
package cryptfs
|
|
||||||
|
|
||||||
// CryptFS is the crypto backend of GoCryptFS
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
PROGRAM_NAME = "gocryptfs"
|
|
||||||
|
|
||||||
DEFAULT_PLAINBS = 4096
|
|
||||||
KEY_LEN = 32 // AES-256
|
|
||||||
AUTH_TAG_LEN = 16
|
|
||||||
DIRIV_LEN = 16 // identical to AES block size
|
|
||||||
DIRIV_FILENAME = "gocryptfs.diriv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CryptFS struct {
|
|
||||||
blockCipher cipher.Block
|
|
||||||
gcm cipher.AEAD
|
|
||||||
gcmIVLen int
|
|
||||||
gcmIVGen nonceGenerator
|
|
||||||
plainBS uint64
|
|
||||||
cipherBS uint64
|
|
||||||
// Stores an all-zero block of size cipherBS
|
|
||||||
allZeroBlock []byte
|
|
||||||
// DirIV cache for filename encryption
|
|
||||||
DirIVCache dirIVCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool, GCMIV128 bool) *CryptFS {
|
|
||||||
|
|
||||||
if len(key) != KEY_LEN {
|
|
||||||
panic(fmt.Sprintf("Unsupported key length %d", len(key)))
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want the IV size in bytes
|
|
||||||
gcmIV := 96 / 8
|
|
||||||
if GCMIV128 {
|
|
||||||
gcmIV = 128 / 8
|
|
||||||
}
|
|
||||||
|
|
||||||
var gcm cipher.AEAD
|
|
||||||
if useOpenssl {
|
|
||||||
gcm = opensslGCM{key}
|
|
||||||
} else {
|
|
||||||
gcm, err = goGCMWrapper(b, gcmIV)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plainBS := DEFAULT_PLAINBS
|
|
||||||
cipherBS := plainBS + gcmIV + AUTH_TAG_LEN
|
|
||||||
|
|
||||||
return &CryptFS{
|
|
||||||
blockCipher: b,
|
|
||||||
gcm: gcm,
|
|
||||||
gcmIVLen: gcmIV,
|
|
||||||
gcmIVGen: nonceGenerator{nonceLen: gcmIV},
|
|
||||||
plainBS: uint64(plainBS),
|
|
||||||
cipherBS: uint64(cipherBS),
|
|
||||||
allZeroBlock: make([]byte, cipherBS),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get plaintext block size
|
|
||||||
func (be *CryptFS) PlainBS() uint64 {
|
|
||||||
return be.plainBS
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-block storage overhead
|
|
||||||
func (be *CryptFS) BlockOverhead() uint64 {
|
|
||||||
return be.cipherBS - be.plainBS
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
package cryptfs
|
|
||||||
|
|
||||||
// Filename encryption / decryption functions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/rfjakob/eme"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DecryptName - decrypt base64-encoded encrypted filename "cipherName"
|
|
||||||
// The used encryption is either CBC or EME, depending on the "EMENames" argument.
|
|
||||||
//
|
|
||||||
// This function is exported because it allows for a very efficient readdir
|
|
||||||
// implementation (read IV once, decrypt all names using this function).
|
|
||||||
func (be *CryptFS) DecryptName(cipherName string, iv []byte, EMENames bool) (string, error) {
|
|
||||||
return be.decryptName(cipherName, iv, EMENames)
|
|
||||||
}
|
|
||||||
|
|
||||||
// decryptName - decrypt base64-encoded encrypted filename "cipherName".
|
|
||||||
// The used encryption is either CBC or EME, depending on the "EMENames" argument.
|
|
||||||
func (be *CryptFS) decryptName(cipherName string, iv []byte, EMENames bool) (string, error) {
|
|
||||||
|
|
||||||
// Make sure relative symlinks still work after encryption
|
|
||||||
// by passing these through unchanged
|
|
||||||
if cipherName == "." || cipherName == ".." {
|
|
||||||
return cipherName, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
bin, err := base64.URLEncoding.DecodeString(cipherName)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(bin)%aes.BlockSize != 0 {
|
|
||||||
return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin))
|
|
||||||
}
|
|
||||||
|
|
||||||
if EMENames {
|
|
||||||
bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionDecrypt)
|
|
||||||
} else {
|
|
||||||
cbc := cipher.NewCBCDecrypter(be.blockCipher, iv)
|
|
||||||
cbc.CryptBlocks(bin, bin)
|
|
||||||
}
|
|
||||||
|
|
||||||
bin, err = be.unPad16(bin)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
plain := string(bin)
|
|
||||||
return plain, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// encryptName - encrypt "plainName", return base64-encoded "cipherName64"
|
|
||||||
// The used encryption is either CBC or EME, depending on the "EMENames" argument.
|
|
||||||
func (be *CryptFS) encryptName(plainName string, iv []byte, EMENames bool) (cipherName64 string) {
|
|
||||||
|
|
||||||
// Make sure relative symlinks still work after encryption
|
|
||||||
// by passing these trough unchanged
|
|
||||||
if plainName == "." || plainName == ".." {
|
|
||||||
return plainName
|
|
||||||
}
|
|
||||||
|
|
||||||
bin := []byte(plainName)
|
|
||||||
bin = be.pad16(bin)
|
|
||||||
|
|
||||||
if EMENames {
|
|
||||||
bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionEncrypt)
|
|
||||||
} else {
|
|
||||||
cbc := cipher.NewCBCEncrypter(be.blockCipher, iv)
|
|
||||||
cbc.CryptBlocks(bin, bin)
|
|
||||||
}
|
|
||||||
|
|
||||||
cipherName64 = base64.URLEncoding.EncodeToString(bin)
|
|
||||||
return cipherName64
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding
|
|
||||||
// https://tools.ietf.org/html/rfc5652#section-6.3
|
|
||||||
func (be *CryptFS) pad16(orig []byte) (padded []byte) {
|
|
||||||
oldLen := len(orig)
|
|
||||||
if oldLen == 0 {
|
|
||||||
panic("Padding zero-length string makes no sense")
|
|
||||||
}
|
|
||||||
padLen := aes.BlockSize - oldLen%aes.BlockSize
|
|
||||||
if padLen == 0 {
|
|
||||||
padLen = aes.BlockSize
|
|
||||||
}
|
|
||||||
newLen := oldLen + padLen
|
|
||||||
padded = make([]byte, newLen)
|
|
||||||
copy(padded, orig)
|
|
||||||
padByte := byte(padLen)
|
|
||||||
for i := oldLen; i < newLen; i++ {
|
|
||||||
padded[i] = padByte
|
|
||||||
}
|
|
||||||
return padded
|
|
||||||
}
|
|
||||||
|
|
||||||
// unPad16 - remove padding
|
|
||||||
func (be *CryptFS) unPad16(orig []byte) ([]byte, error) {
|
|
||||||
oldLen := len(orig)
|
|
||||||
if oldLen%aes.BlockSize != 0 {
|
|
||||||
return nil, errors.New("Unaligned size")
|
|
||||||
}
|
|
||||||
// The last byte is always a padding byte
|
|
||||||
padByte := orig[oldLen-1]
|
|
||||||
// The padding byte's value is the padding length
|
|
||||||
padLen := int(padByte)
|
|
||||||
// Padding must be at least 1 byte
|
|
||||||
if padLen <= 0 {
|
|
||||||
return nil, errors.New("Padding cannot be zero-length")
|
|
||||||
}
|
|
||||||
// Larger paddings make no sense
|
|
||||||
if padLen > aes.BlockSize {
|
|
||||||
return nil, errors.New("Padding cannot be larger than 16")
|
|
||||||
}
|
|
||||||
// All padding bytes must be identical
|
|
||||||
for i := oldLen - padLen; i < oldLen; i++ {
|
|
||||||
if orig[i] != padByte {
|
|
||||||
return nil, errors.New(fmt.Sprintf("Padding byte at i=%d is invalid", i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newLen := oldLen - padLen
|
|
||||||
// Padding an empty string makes no sense
|
|
||||||
if newLen == 0 {
|
|
||||||
return nil, errors.New("Unpadded length is zero")
|
|
||||||
}
|
|
||||||
return orig[0:newLen], nil
|
|
||||||
}
|
|
@ -7,7 +7,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test -init flag
|
// Test -init flag
|
||||||
@ -26,7 +27,7 @@ func TestInit(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = os.Stat(dir + cryptfs.ConfDefaultName)
|
_, err = os.Stat(dir + configfile.ConfDefaultName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -96,22 +97,22 @@ func TestInitPlaintextNames(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = os.Stat(dir + cryptfs.ConfDefaultName)
|
_, err = os.Stat(dir + configfile.ConfDefaultName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = os.Stat(dir + cryptfs.DIRIV_FILENAME)
|
_, err = os.Stat(dir + nametransform.DirIVFilename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("gocryptfs.diriv should not have been created with -plaintextnames")
|
t.Errorf("gocryptfs.diriv should not have been created with -plaintextnames")
|
||||||
}
|
}
|
||||||
_, cf, err := cryptfs.LoadConfFile(dir+cryptfs.ConfDefaultName, "test")
|
_, cf, err := configfile.LoadConfFile(dir+configfile.ConfDefaultName, "test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !cf.IsFeatureFlagSet(cryptfs.FlagPlaintextNames) {
|
if !cf.IsFeatureFlagSet(configfile.FlagPlaintextNames) {
|
||||||
t.Error("PlaintextNames flag should be set but isnt")
|
t.Error("PlaintextNames flag should be set but isnt")
|
||||||
}
|
}
|
||||||
if cf.IsFeatureFlagSet(cryptfs.FlagEMENames) || cf.IsFeatureFlagSet(cryptfs.FlagDirIV) {
|
if cf.IsFeatureFlagSet(configfile.FlagEMENames) || cf.IsFeatureFlagSet(configfile.FlagDirIV) {
|
||||||
t.Error("FlagEMENames and FlagDirIV should be not set")
|
t.Error("FlagEMENames and FlagDirIV should be not set")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Note: the code assumes that all have a trailing slash
|
// Note: the code assumes that all have a trailing slash
|
||||||
@ -42,7 +42,7 @@ func resetTmpDir() {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err = cryptfs.WriteDirIV(defaultCipherDir)
|
err = nametransform.WriteDirIV(defaultCipherDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -58,9 +58,9 @@ func mount(c string, p string, extraArgs ...string) {
|
|||||||
args = append(args, c)
|
args = append(args, c)
|
||||||
args = append(args, p)
|
args = append(args, p)
|
||||||
cmd := exec.Command(gocryptfsBinary, args...)
|
cmd := exec.Command(gocryptfsBinary, args...)
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
if testing.Verbose() {
|
if testing.Verbose() {
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
}
|
}
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package cryptfs
|
package configfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/contentenc"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
)
|
)
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
@ -36,10 +40,10 @@ type ConfFile struct {
|
|||||||
func CreateConfFile(filename string, password string, plaintextNames bool, logN int) error {
|
func CreateConfFile(filename string, password string, plaintextNames bool, logN int) error {
|
||||||
var cf ConfFile
|
var cf ConfFile
|
||||||
cf.filename = filename
|
cf.filename = filename
|
||||||
cf.Version = HEADER_CURRENT_VERSION
|
cf.Version = contentenc.CurrentVersion
|
||||||
|
|
||||||
// Generate new random master key
|
// Generate new random master key
|
||||||
key := RandBytes(KEY_LEN)
|
key := cryptocore.RandBytes(cryptocore.KeyLen)
|
||||||
|
|
||||||
// Encrypt it using the password
|
// Encrypt it using the password
|
||||||
// This sets ScryptObject and EncryptedKey
|
// This sets ScryptObject and EncryptedKey
|
||||||
@ -75,11 +79,11 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
|
|||||||
// Unmarshal
|
// Unmarshal
|
||||||
err = json.Unmarshal(js, &cf)
|
err = json.Unmarshal(js, &cf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Warn.Printf("Failed to unmarshal config file")
|
toggledlog.Warn.Printf("Failed to unmarshal config file")
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cf.Version != HEADER_CURRENT_VERSION {
|
if cf.Version != contentenc.CurrentVersion {
|
||||||
return nil, nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version)
|
return nil, nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,11 +99,13 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
|
|||||||
// Unlock master key using password-based key
|
// Unlock master key using password-based key
|
||||||
// We use stock go GCM instead of OpenSSL here as speed is not important
|
// We use stock go GCM instead of OpenSSL here as speed is not important
|
||||||
// and we get better error messages
|
// and we get better error messages
|
||||||
cfs := NewCryptFS(scryptHash, false, false, false)
|
cc := cryptocore.New(scryptHash, false, false)
|
||||||
key, err := cfs.DecryptBlock(cf.EncryptedKey, 0, nil)
|
ce := contentenc.New(cc, 4096)
|
||||||
|
|
||||||
|
key, err := ce.DecryptBlock(cf.EncryptedKey, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Warn.Printf("failed to unlock master key: %s", err.Error())
|
toggledlog.Warn.Printf("failed to unlock master key: %s", err.Error())
|
||||||
Warn.Printf("Password incorrect.")
|
toggledlog.Warn.Printf("Password incorrect.")
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,8 +122,9 @@ func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
|
|||||||
scryptHash := cf.ScryptObject.DeriveKey(password)
|
scryptHash := cf.ScryptObject.DeriveKey(password)
|
||||||
|
|
||||||
// Lock master key using password-based key
|
// Lock master key using password-based key
|
||||||
cfs := NewCryptFS(scryptHash, false, false, false)
|
cc := cryptocore.New(scryptHash, false, false)
|
||||||
cf.EncryptedKey = cfs.EncryptBlock(key, 0, nil)
|
ce := contentenc.New(cc, 4096)
|
||||||
|
cf.EncryptedKey = ce.EncryptBlock(key, 0, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteFile - write out config in JSON format to file "filename.tmp"
|
// WriteFile - write out config in JSON format to file "filename.tmp"
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package configfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -1,16 +1,19 @@
|
|||||||
package cryptfs
|
package configfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/scrypt"
|
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/scrypt"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// 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
|
||||||
SCRYPT_DEFAULT_LOGN = 16
|
ScryptDefaultLogN = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
type scryptKdf struct {
|
type scryptKdf struct {
|
||||||
@ -23,9 +26,9 @@ type scryptKdf struct {
|
|||||||
|
|
||||||
func NewScryptKdf(logN int) scryptKdf {
|
func NewScryptKdf(logN int) scryptKdf {
|
||||||
var s scryptKdf
|
var s scryptKdf
|
||||||
s.Salt = RandBytes(KEY_LEN)
|
s.Salt = cryptocore.RandBytes(cryptocore.KeyLen)
|
||||||
if logN <= 0 {
|
if logN <= 0 {
|
||||||
s.N = 1 << SCRYPT_DEFAULT_LOGN
|
s.N = 1 << ScryptDefaultLogN
|
||||||
} else {
|
} else {
|
||||||
if logN < 10 {
|
if logN < 10 {
|
||||||
fmt.Println("Error: scryptn below 10 is too low to make sense. Aborting.")
|
fmt.Println("Error: scryptn below 10 is too low to make sense. Aborting.")
|
||||||
@ -35,7 +38,7 @@ func NewScryptKdf(logN int) scryptKdf {
|
|||||||
}
|
}
|
||||||
s.R = 8 // Always 8
|
s.R = 8 // Always 8
|
||||||
s.P = 1 // Always 1
|
s.P = 1 // Always 1
|
||||||
s.KeyLen = KEY_LEN
|
s.KeyLen = cryptocore.KeyLen
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package configfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
@ -1,31 +1,18 @@
|
|||||||
package cryptfs
|
package contentenc
|
||||||
|
|
||||||
// File content encryption / decryption
|
// File content encryption / decryption
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// md5sum - debug helper, return md5 hex string
|
|
||||||
func md5sum(buf []byte) string {
|
|
||||||
rawHash := md5.Sum(buf)
|
|
||||||
hash := hex.EncodeToString(rawHash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
type CryptFile struct {
|
|
||||||
file *os.File
|
|
||||||
gcm cipher.AEAD
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptBlocks - Decrypt a number of blocks
|
// DecryptBlocks - Decrypt a number of blocks
|
||||||
func (be *CryptFS) 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
|
||||||
@ -46,7 +33,7 @@ func (be *CryptFS) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId
|
|||||||
//
|
//
|
||||||
// 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 *CryptFS) 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 {
|
||||||
@ -55,30 +42,30 @@ func (be *CryptFS) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte
|
|||||||
|
|
||||||
// All-zero block?
|
// All-zero block?
|
||||||
if bytes.Equal(ciphertext, be.allZeroBlock) {
|
if bytes.Equal(ciphertext, be.allZeroBlock) {
|
||||||
Debug.Printf("DecryptBlock: file hole encountered")
|
toggledlog.Debug.Printf("DecryptBlock: file hole encountered")
|
||||||
return make([]byte, be.plainBS), nil
|
return make([]byte, be.plainBS), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ciphertext) < be.gcmIVLen {
|
if len(ciphertext) < be.cryptoCore.IVLen {
|
||||||
Warn.Printf("DecryptBlock: Block is too short: %d bytes", len(ciphertext))
|
toggledlog.Warn.Printf("DecryptBlock: Block is too short: %d bytes", len(ciphertext))
|
||||||
return nil, errors.New("Block is too short")
|
return nil, errors.New("Block is too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract nonce
|
// Extract nonce
|
||||||
nonce := ciphertext[:be.gcmIVLen]
|
nonce := ciphertext[:be.cryptoCore.IVLen]
|
||||||
ciphertextOrig := ciphertext
|
ciphertextOrig := ciphertext
|
||||||
ciphertext = ciphertext[be.gcmIVLen:]
|
ciphertext = ciphertext[be.cryptoCore.IVLen:]
|
||||||
|
|
||||||
// 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.gcm.Open(plaintext, nonce, ciphertext, aData)
|
plaintext, err := be.cryptoCore.Gcm.Open(plaintext, nonce, ciphertext, aData)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Warn.Printf("DecryptBlock: %s, len=%d, md5=%s", err.Error(), len(ciphertextOrig), md5sum(ciphertextOrig))
|
toggledlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig))
|
||||||
Debug.Println(hex.Dump(ciphertextOrig))
|
toggledlog.Debug.Println(hex.Dump(ciphertextOrig))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +73,7 @@ func (be *CryptFS) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encryptBlock - Encrypt and add IV and MAC
|
// encryptBlock - Encrypt and add IV and MAC
|
||||||
func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte) []byte {
|
func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte) []byte {
|
||||||
|
|
||||||
// Empty block?
|
// Empty block?
|
||||||
if len(plaintext) == 0 {
|
if len(plaintext) == 0 {
|
||||||
@ -94,7 +81,7 @@ func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get fresh nonce
|
// Get fresh nonce
|
||||||
nonce := be.gcmIVGen.Get()
|
nonce := be.cryptoCore.GcmIVGen.Get()
|
||||||
|
|
||||||
// Authenticate block with block number and file ID
|
// Authenticate block with block number and file ID
|
||||||
aData := make([]byte, 8)
|
aData := make([]byte, 8)
|
||||||
@ -102,14 +89,14 @@ func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte)
|
|||||||
aData = append(aData, fileID...)
|
aData = append(aData, fileID...)
|
||||||
|
|
||||||
// Encrypt plaintext and append to nonce
|
// Encrypt plaintext and append to nonce
|
||||||
ciphertext := be.gcm.Seal(nonce, nonce, plaintext, aData)
|
ciphertext := be.cryptoCore.Gcm.Seal(nonce, nonce, plaintext, aData)
|
||||||
|
|
||||||
return ciphertext
|
return ciphertext
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeBlocks - Merge newData into oldData at offset
|
// MergeBlocks - Merge newData into oldData at offset
|
||||||
// New block may be bigger than both newData and oldData
|
// New block may be bigger than both newData and oldData
|
||||||
func (be *CryptFS) MergeBlocks(oldData []byte, newData []byte, offset int) []byte {
|
func (be *ContentEnc) MergeBlocks(oldData []byte, newData []byte, offset int) []byte {
|
||||||
|
|
||||||
// Make block of maximum size
|
// Make block of maximum size
|
||||||
out := make([]byte, be.plainBS)
|
out := make([]byte, be.plainBS)
|
31
internal/contentenc/content_api.go
Normal file
31
internal/contentenc/content_api.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package contentenc
|
||||||
|
|
||||||
|
import "github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
|
|
||||||
|
type ContentEnc struct {
|
||||||
|
// Cryptographic primitives
|
||||||
|
cryptoCore *cryptocore.CryptoCore
|
||||||
|
// Plaintext block size
|
||||||
|
plainBS uint64
|
||||||
|
// Ciphertext block size
|
||||||
|
cipherBS uint64
|
||||||
|
// All-zero block of size cipherBS, for fast compares
|
||||||
|
allZeroBlock []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc {
|
||||||
|
|
||||||
|
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
|
||||||
|
|
||||||
|
return &ContentEnc{
|
||||||
|
cryptoCore: cc,
|
||||||
|
plainBS: plainBS,
|
||||||
|
cipherBS: cipherBS,
|
||||||
|
allZeroBlock: make([]byte, cipherBS),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (be *ContentEnc) PlainBS() uint64 {
|
||||||
|
return be.plainBS
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package contentenc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package contentenc
|
||||||
|
|
||||||
// Per-file header
|
// Per-file header
|
||||||
//
|
//
|
||||||
@ -7,10 +7,14 @@ package cryptfs
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HEADER_CURRENT_VERSION = 2 // Current on-disk-format version
|
// Current On-Disk-Format version
|
||||||
|
CurrentVersion = 2
|
||||||
|
|
||||||
HEADER_VERSION_LEN = 2 // uint16
|
HEADER_VERSION_LEN = 2 // uint16
|
||||||
HEADER_ID_LEN = 16 // 128 bit random file id
|
HEADER_ID_LEN = 16 // 128 bit random file id
|
||||||
HEADER_LEN = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length
|
HEADER_LEN = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length
|
||||||
@ -23,7 +27,7 @@ type FileHeader struct {
|
|||||||
|
|
||||||
// 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 != HEADER_CURRENT_VERSION {
|
if len(h.Id) != HEADER_ID_LEN || h.Version != CurrentVersion {
|
||||||
panic("FileHeader object not properly initialized")
|
panic("FileHeader object not properly initialized")
|
||||||
}
|
}
|
||||||
buf := make([]byte, HEADER_LEN)
|
buf := make([]byte, HEADER_LEN)
|
||||||
@ -40,8 +44,8 @@ func ParseHeader(buf []byte) (*FileHeader, error) {
|
|||||||
}
|
}
|
||||||
var h FileHeader
|
var h FileHeader
|
||||||
h.Version = binary.BigEndian.Uint16(buf[0:HEADER_VERSION_LEN])
|
h.Version = binary.BigEndian.Uint16(buf[0:HEADER_VERSION_LEN])
|
||||||
if h.Version != HEADER_CURRENT_VERSION {
|
if h.Version != CurrentVersion {
|
||||||
return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, HEADER_CURRENT_VERSION)
|
return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, CurrentVersion)
|
||||||
}
|
}
|
||||||
h.Id = buf[HEADER_VERSION_LEN:]
|
h.Id = buf[HEADER_VERSION_LEN:]
|
||||||
return &h, nil
|
return &h, nil
|
||||||
@ -50,7 +54,7 @@ func ParseHeader(buf []byte) (*FileHeader, error) {
|
|||||||
// RandomHeader - create new fileHeader object with random Id
|
// RandomHeader - create new fileHeader object with random Id
|
||||||
func RandomHeader() *FileHeader {
|
func RandomHeader() *FileHeader {
|
||||||
var h FileHeader
|
var h FileHeader
|
||||||
h.Version = HEADER_CURRENT_VERSION
|
h.Version = CurrentVersion
|
||||||
h.Id = RandBytes(HEADER_ID_LEN)
|
h.Id = cryptocore.RandBytes(HEADER_ID_LEN)
|
||||||
return &h
|
return &h
|
||||||
}
|
}
|
@ -1,11 +1,11 @@
|
|||||||
package cryptfs
|
package contentenc
|
||||||
|
|
||||||
// intraBlock identifies a part of a file block
|
// intraBlock identifies a part of a file block
|
||||||
type intraBlock struct {
|
type intraBlock struct {
|
||||||
BlockNo uint64 // Block number in file
|
BlockNo uint64 // Block number in file
|
||||||
Skip uint64 // Offset into block plaintext
|
Skip uint64 // Offset into block plaintext
|
||||||
Length uint64 // Length of data from this block
|
Length uint64 // Length of data from this block
|
||||||
fs *CryptFS
|
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.
|
@ -1,29 +1,33 @@
|
|||||||
package cryptfs
|
package contentenc
|
||||||
|
|
||||||
// CryptFS methods that translate offsets between ciphertext and plaintext
|
import (
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Contentenc methods that translate offsets between ciphertext and plaintext
|
||||||
|
|
||||||
// get the block number at plain-text offset
|
// get the block number at plain-text offset
|
||||||
func (be *CryptFS) PlainOffToBlockNo(plainOffset uint64) uint64 {
|
func (be *ContentEnc) PlainOffToBlockNo(plainOffset uint64) uint64 {
|
||||||
return plainOffset / be.plainBS
|
return plainOffset / be.plainBS
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the block number at ciphter-text offset
|
// get the block number at ciphter-text offset
|
||||||
func (be *CryptFS) CipherOffToBlockNo(cipherOffset uint64) uint64 {
|
func (be *ContentEnc) CipherOffToBlockNo(cipherOffset uint64) uint64 {
|
||||||
return (cipherOffset - HEADER_LEN) / be.cipherBS
|
return (cipherOffset - HEADER_LEN) / be.cipherBS
|
||||||
}
|
}
|
||||||
|
|
||||||
// get ciphertext offset of block "blockNo"
|
// get ciphertext offset of block "blockNo"
|
||||||
func (be *CryptFS) BlockNoToCipherOff(blockNo uint64) uint64 {
|
func (be *ContentEnc) BlockNoToCipherOff(blockNo uint64) uint64 {
|
||||||
return HEADER_LEN + blockNo*be.cipherBS
|
return HEADER_LEN + blockNo*be.cipherBS
|
||||||
}
|
}
|
||||||
|
|
||||||
// get plaintext offset of block "blockNo"
|
// get plaintext offset of block "blockNo"
|
||||||
func (be *CryptFS) 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
|
// PlainSize - calculate plaintext size from ciphertext size
|
||||||
func (be *CryptFS) 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 {
|
||||||
@ -31,12 +35,12 @@ func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cipherSize == HEADER_LEN {
|
if cipherSize == HEADER_LEN {
|
||||||
Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize)
|
toggledlog.Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if cipherSize < HEADER_LEN {
|
if cipherSize < HEADER_LEN {
|
||||||
Warn.Printf("cipherSize %d < header size: corrupt file\n", cipherSize)
|
toggledlog.Warn.Printf("cipherSize %d < header size: corrupt file\n", cipherSize)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +54,7 @@ func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CipherSize - calculate ciphertext size from plaintext size
|
// CipherSize - calculate ciphertext size from plaintext size
|
||||||
func (be *CryptFS) PlainSizeToCipherSize(plainSize uint64) uint64 {
|
func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 {
|
||||||
|
|
||||||
// Block number at last byte
|
// Block number at last byte
|
||||||
blockNo := be.PlainOffToBlockNo(plainSize - 1)
|
blockNo := be.PlainOffToBlockNo(plainSize - 1)
|
||||||
@ -62,7 +66,7 @@ func (be *CryptFS) PlainSizeToCipherSize(plainSize uint64) uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Split a plaintext byte range into (possibly partial) blocks
|
// Split a plaintext byte range into (possibly partial) blocks
|
||||||
func (be *CryptFS) 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
|
||||||
@ -81,6 +85,10 @@ func (be *CryptFS) ExplodePlainRange(offset uint64, length uint64) []intraBlock
|
|||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (be *ContentEnc) BlockOverhead() uint64 {
|
||||||
|
return be.cipherBS - be.plainBS
|
||||||
|
}
|
||||||
|
|
||||||
func MinUint64(x uint64, y uint64) uint64 {
|
func MinUint64(x uint64, y uint64) uint64 {
|
||||||
if x < y {
|
if x < y {
|
||||||
return x
|
return x
|
56
internal/cryptocore/crypto_api.go
Normal file
56
internal/cryptocore/crypto_api.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package cryptocore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/aes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyLen = 32 // AES-256
|
||||||
|
AuthTagLen = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
type CryptoCore struct {
|
||||||
|
BlockCipher cipher.Block
|
||||||
|
Gcm cipher.AEAD
|
||||||
|
GcmIVGen *nonceGenerator
|
||||||
|
IVLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(key []byte, useOpenssl bool, GCMIV128 bool) *CryptoCore {
|
||||||
|
|
||||||
|
if len(key) != KeyLen {
|
||||||
|
panic(fmt.Sprintf("Unsupported key length %d", len(key)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want the IV size in bytes
|
||||||
|
IVLen := 96 / 8
|
||||||
|
if GCMIV128 {
|
||||||
|
IVLen = 128 / 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// We always use built-in Go crypto for blockCipher because it is not
|
||||||
|
// performance-critical.
|
||||||
|
blockCipher, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gcm cipher.AEAD
|
||||||
|
if useOpenssl {
|
||||||
|
gcm = opensslGCM{key}
|
||||||
|
} else {
|
||||||
|
gcm, err = goGCMWrapper(blockCipher, IVLen)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CryptoCore{
|
||||||
|
BlockCipher: blockCipher,
|
||||||
|
Gcm: gcm,
|
||||||
|
GcmIVGen: &nonceGenerator{nonceLen: IVLen},
|
||||||
|
IVLen: IVLen,
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
// +build !go1.5
|
// +build !go1.5
|
||||||
// = go 1.4 or lower
|
// = go 1.4 or lower
|
||||||
|
|
||||||
package cryptfs
|
package cryptocore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
@ -1,7 +1,7 @@
|
|||||||
// +build go1.5
|
// +build go1.5
|
||||||
// = go 1.5 or higher
|
// = go 1.5 or higher
|
||||||
|
|
||||||
package cryptfs
|
package cryptocore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package cryptocore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get "n" random bytes from /dev/urandom or panic
|
// Get "n" random bytes from /dev/urandom or panic
|
||||||
@ -29,10 +31,10 @@ type nonceGenerator struct {
|
|||||||
nonceLen int // bytes
|
nonceLen int // bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a random 96 bit nonce
|
// Get a random "nonceLen"-byte nonce
|
||||||
func (n *nonceGenerator) Get() []byte {
|
func (n *nonceGenerator) Get() []byte {
|
||||||
nonce := RandBytes(n.nonceLen)
|
nonce := RandBytes(n.nonceLen)
|
||||||
Debug.Printf("nonceGenerator.Get(): %s\n", hex.EncodeToString(nonce))
|
toggledlog.Debug.Printf("nonceGenerator.Get(): %s\n", hex.EncodeToString(nonce))
|
||||||
if bytes.Equal(nonce, n.lastNonce) {
|
if bytes.Equal(nonce, n.lastNonce) {
|
||||||
m := fmt.Sprintf("Got the same nonce twice: %s. This should never happen!", hex.EncodeToString(nonce))
|
m := fmt.Sprintf("Got the same nonce twice: %s. This should never happen!", hex.EncodeToString(nonce))
|
||||||
panic(m)
|
panic(m)
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package cryptocore
|
||||||
|
|
||||||
// Implements cipher.AEAD with OpenSSL backend
|
// Implements cipher.AEAD with OpenSSL backend
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ type opensslGCM struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (be opensslGCM) Overhead() int {
|
func (be opensslGCM) Overhead() int {
|
||||||
return AUTH_TAG_LEN
|
return AuthTagLen
|
||||||
}
|
}
|
||||||
|
|
||||||
func (be opensslGCM) NonceSize() int {
|
func (be opensslGCM) NonceSize() int {
|
||||||
@ -28,11 +28,11 @@ func (be opensslGCM) Seal(dst, nonce, plaintext, data []byte) []byte {
|
|||||||
|
|
||||||
// Preallocate output buffer
|
// Preallocate output buffer
|
||||||
var cipherBuf bytes.Buffer
|
var cipherBuf bytes.Buffer
|
||||||
cipherBuf.Grow(len(dst) + len(plaintext) + AUTH_TAG_LEN)
|
cipherBuf.Grow(len(dst) + len(plaintext) + AuthTagLen)
|
||||||
// Output will be appended to dst
|
// Output will be appended to dst
|
||||||
cipherBuf.Write(dst)
|
cipherBuf.Write(dst)
|
||||||
|
|
||||||
ectx, err := openssl.NewGCMEncryptionCipherCtx(KEY_LEN*8, nil, be.key, nonce)
|
ectx, err := openssl.NewGCMEncryptionCipherCtx(KeyLen*8, nil, be.key, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -69,11 +69,11 @@ func (be opensslGCM) Seal(dst, nonce, plaintext, data []byte) []byte {
|
|||||||
func (be opensslGCM) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
func (be opensslGCM) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||||
|
|
||||||
l := len(ciphertext)
|
l := len(ciphertext)
|
||||||
tag := ciphertext[l-AUTH_TAG_LEN : l]
|
tag := ciphertext[l-AuthTagLen : l]
|
||||||
ciphertext = ciphertext[0 : l-AUTH_TAG_LEN]
|
ciphertext = ciphertext[0 : l-AuthTagLen]
|
||||||
plainBuf := bytes.NewBuffer(dst)
|
plainBuf := bytes.NewBuffer(dst)
|
||||||
|
|
||||||
dctx, err := openssl.NewGCMDecryptionCipherCtx(KEY_LEN*8, nil, be.key, nonce)
|
dctx, err := openssl.NewGCMDecryptionCipherCtx(KeyLen*8, nil, be.key, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package cryptocore
|
||||||
|
|
||||||
// Benchmark go built-int GCM against spacemonkey openssl bindings
|
// Benchmark go built-int GCM against spacemonkey openssl bindings
|
||||||
//
|
//
|
16
internal/nametransform/name_api.go
Normal file
16
internal/nametransform/name_api.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package nametransform
|
||||||
|
|
||||||
|
import "github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
|
|
||||||
|
type NameTransform struct {
|
||||||
|
cryptoCore *cryptocore.CryptoCore
|
||||||
|
useEME bool
|
||||||
|
DirIVCache dirIVCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(c *cryptocore.CryptoCore, useEME bool) *NameTransform {
|
||||||
|
return &NameTransform{
|
||||||
|
cryptoCore: c,
|
||||||
|
useEME: useEME,
|
||||||
|
}
|
||||||
|
}
|
63
internal/nametransform/names_core.go
Normal file
63
internal/nametransform/names_core.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package nametransform
|
||||||
|
|
||||||
|
// Filename encryption / decryption functions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rfjakob/eme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecryptName - decrypt base64-encoded encrypted filename "cipherName"
|
||||||
|
// The used encryption is either CBC or EME, depending on "useEME".
|
||||||
|
//
|
||||||
|
// This function is exported because it allows for a very efficient readdir
|
||||||
|
// implementation (read IV once, decrypt all names using this function).
|
||||||
|
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
|
||||||
|
|
||||||
|
bin, err := base64.URLEncoding.DecodeString(cipherName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bin)%aes.BlockSize != 0 {
|
||||||
|
return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin))
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.useEME {
|
||||||
|
bin = eme.Transform(n.cryptoCore.BlockCipher, iv, bin, eme.DirectionDecrypt)
|
||||||
|
} else {
|
||||||
|
cbc := cipher.NewCBCDecrypter(n.cryptoCore.BlockCipher, iv)
|
||||||
|
cbc.CryptBlocks(bin, bin)
|
||||||
|
}
|
||||||
|
|
||||||
|
bin, err = unPad16(bin)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
plain := string(bin)
|
||||||
|
return plain, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// encryptName - encrypt "plainName", return base64-encoded "cipherName64"
|
||||||
|
// The used encryption is either CBC or EME, depending on "useEME".
|
||||||
|
func (n *NameTransform) encryptName(plainName string, iv []byte) (cipherName64 string) {
|
||||||
|
|
||||||
|
bin := []byte(plainName)
|
||||||
|
bin = pad16(bin)
|
||||||
|
|
||||||
|
if n.useEME {
|
||||||
|
bin = eme.Transform(n.cryptoCore.BlockCipher, iv, bin, eme.DirectionEncrypt)
|
||||||
|
} else {
|
||||||
|
cbc := cipher.NewCBCEncrypter(n.cryptoCore.BlockCipher, iv)
|
||||||
|
cbc.CryptBlocks(bin, bin)
|
||||||
|
}
|
||||||
|
|
||||||
|
cipherName64 = base64.URLEncoding.EncodeToString(bin)
|
||||||
|
return cipherName64
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package nametransform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,6 +7,17 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 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 = "gocryptfs.diriv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A simple one-entry DirIV cache
|
// A simple one-entry DirIV cache
|
||||||
@ -50,23 +61,23 @@ func (c *dirIVCache) Clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path)
|
// readDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path)
|
||||||
func (be *CryptFS) ReadDirIV(dir string) (iv []byte, readErr error) {
|
func (be *NameTransform) ReadDirIV(dir string) (iv []byte, readErr error) {
|
||||||
ivfile := filepath.Join(dir, DIRIV_FILENAME)
|
ivfile := filepath.Join(dir, DirIVFilename)
|
||||||
Debug.Printf("ReadDirIV: reading %s\n", ivfile)
|
toggledlog.Debug.Printf("ReadDirIV: reading %s\n", ivfile)
|
||||||
iv, readErr = ioutil.ReadFile(ivfile)
|
iv, readErr = ioutil.ReadFile(ivfile)
|
||||||
if readErr != nil {
|
if readErr != nil {
|
||||||
// The directory may have been concurrently deleted or moved. Failure to
|
// The directory may have been concurrently deleted or moved. Failure to
|
||||||
// read the diriv is not an error in that case.
|
// read the diriv is not an error in that case.
|
||||||
_, statErr := os.Stat(dir)
|
_, statErr := os.Stat(dir)
|
||||||
if os.IsNotExist(statErr) {
|
if os.IsNotExist(statErr) {
|
||||||
Debug.Printf("ReadDirIV: Dir %s was deleted under our feet", dir)
|
toggledlog.Debug.Printf("ReadDirIV: Dir %s was deleted under our feet", dir)
|
||||||
} else {
|
} else {
|
||||||
// This should not happen
|
// This should not happen
|
||||||
Warn.Printf("ReadDirIV: Dir exists but diriv does not: %v\n", readErr)
|
toggledlog.Warn.Printf("ReadDirIV: Dir exists but diriv does not: %v\n", readErr)
|
||||||
}
|
}
|
||||||
return nil, readErr
|
return nil, readErr
|
||||||
}
|
}
|
||||||
if len(iv) != DIRIV_LEN {
|
if len(iv) != dirIVLen {
|
||||||
return nil, fmt.Errorf("ReadDirIV: Invalid length %d\n", len(iv))
|
return nil, fmt.Errorf("ReadDirIV: Invalid length %d\n", len(iv))
|
||||||
}
|
}
|
||||||
return iv, nil
|
return iv, nil
|
||||||
@ -76,14 +87,14 @@ func (be *CryptFS) ReadDirIV(dir string) (iv []byte, readErr error) {
|
|||||||
// This function is exported because it is used from pathfs_frontend, main,
|
// This function is exported because it is used from pathfs_frontend, main,
|
||||||
// and also the automated tests.
|
// and also the automated tests.
|
||||||
func WriteDirIV(dir string) error {
|
func WriteDirIV(dir string) error {
|
||||||
iv := RandBytes(DIRIV_LEN)
|
iv := cryptocore.RandBytes(dirIVLen)
|
||||||
file := filepath.Join(dir, DIRIV_FILENAME)
|
file := filepath.Join(dir, DirIVFilename)
|
||||||
// 0444 permissions: the file is not secret but should not be written to
|
// 0444 permissions: the file is not secret but should not be written to
|
||||||
return ioutil.WriteFile(file, iv, 0444)
|
return ioutil.WriteFile(file, iv, 0444)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptPathDirIV - encrypt path using EME with DirIV
|
// EncryptPathDirIV - encrypt path using EME with DirIV
|
||||||
func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool) (cipherPath string, err error) {
|
func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cipherPath string, err error) {
|
||||||
// Empty string means root directory
|
// Empty string means root directory
|
||||||
if plainPath == "" {
|
if plainPath == "" {
|
||||||
return plainPath, nil
|
return plainPath, nil
|
||||||
@ -94,7 +105,7 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool)
|
|||||||
if found {
|
if found {
|
||||||
//fmt.Print("h")
|
//fmt.Print("h")
|
||||||
baseName := filepath.Base(plainPath)
|
baseName := filepath.Base(plainPath)
|
||||||
cBaseName := be.encryptName(baseName, iv, eme)
|
cBaseName := be.encryptName(baseName, iv)
|
||||||
cipherPath = cParentDir + "/" + cBaseName
|
cipherPath = cParentDir + "/" + cBaseName
|
||||||
return cipherPath, nil
|
return cipherPath, nil
|
||||||
}
|
}
|
||||||
@ -107,7 +118,7 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
encryptedName := be.encryptName(plainName, iv, eme)
|
encryptedName := be.encryptName(plainName, iv)
|
||||||
encryptedNames = append(encryptedNames, encryptedName)
|
encryptedNames = append(encryptedNames, encryptedName)
|
||||||
wd = filepath.Join(wd, encryptedName)
|
wd = filepath.Join(wd, encryptedName)
|
||||||
}
|
}
|
||||||
@ -119,17 +130,17 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DecryptPathDirIV - decrypt path using EME with DirIV
|
// DecryptPathDirIV - decrypt path using EME with DirIV
|
||||||
func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) {
|
func (be *NameTransform) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) {
|
||||||
var wd = rootDir
|
var wd = rootDir
|
||||||
var plainNames []string
|
var plainNames []string
|
||||||
encryptedNames := strings.Split(encryptedPath, "/")
|
encryptedNames := strings.Split(encryptedPath, "/")
|
||||||
Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames)
|
toggledlog.Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames)
|
||||||
for _, encryptedName := range encryptedNames {
|
for _, encryptedName := range encryptedNames {
|
||||||
iv, err := be.ReadDirIV(wd)
|
iv, err := be.ReadDirIV(wd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
plainName, err := be.decryptName(encryptedName, iv, eme)
|
plainName, err := be.DecryptName(encryptedName, iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package nametransform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
@ -12,7 +12,7 @@ const (
|
|||||||
// DecryptPathNoIV - decrypt path using CBC without any IV.
|
// DecryptPathNoIV - decrypt path using CBC without any IV.
|
||||||
// This function is deprecated by the the more secure DirIV variant and only retained
|
// This function is deprecated by the the more secure DirIV variant and only retained
|
||||||
// for compatability with old filesystems.
|
// for compatability with old filesystems.
|
||||||
func (be *CryptFS) DecryptPathNoIV(cipherPath string) (plainPath string, err error) {
|
func (be *NameTransform) DecryptPathNoIV(cipherPath string) (plainPath string, err error) {
|
||||||
plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt)
|
plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt)
|
||||||
return plainPath, err
|
return plainPath, err
|
||||||
}
|
}
|
||||||
@ -20,14 +20,14 @@ func (be *CryptFS) DecryptPathNoIV(cipherPath string) (plainPath string, err err
|
|||||||
// EncryptPathNoIV - decrypt path using CBC without any IV.
|
// EncryptPathNoIV - decrypt path using CBC without any IV.
|
||||||
// This function is deprecated by the the more secure DirIV variant and only retained
|
// This function is deprecated by the the more secure DirIV variant and only retained
|
||||||
// for compatability with old filesystems.
|
// for compatability with old filesystems.
|
||||||
func (be *CryptFS) EncryptPathNoIV(plainPath string) (cipherPath string) {
|
func (be *NameTransform) EncryptPathNoIV(plainPath string) (cipherPath string) {
|
||||||
cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt)
|
cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt)
|
||||||
return cipherPath
|
return cipherPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV.
|
// translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV.
|
||||||
// Just splits the string on "/" and hands the parts to encryptName() / decryptName()
|
// Just splits the string on "/" and hands the parts to encryptName() / decryptName()
|
||||||
func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) {
|
func (be *NameTransform) translatePathNoIV(path string, op int) (string, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Empty string means root directory
|
// Empty string means root directory
|
||||||
@ -35,7 +35,7 @@ func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) {
|
|||||||
return path, err
|
return path, err
|
||||||
}
|
}
|
||||||
|
|
||||||
zeroIV := make([]byte, DIRIV_LEN)
|
zeroIV := make([]byte, dirIVLen)
|
||||||
|
|
||||||
// Run operation on each path component
|
// Run operation on each path component
|
||||||
var translatedParts []string
|
var translatedParts []string
|
||||||
@ -49,9 +49,9 @@ func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) {
|
|||||||
}
|
}
|
||||||
var newPart string
|
var newPart string
|
||||||
if op == OpEncrypt {
|
if op == OpEncrypt {
|
||||||
newPart = be.encryptName(part, zeroIV, false)
|
newPart = be.encryptName(part, zeroIV)
|
||||||
} else {
|
} else {
|
||||||
newPart, err = be.decryptName(part, zeroIV, false)
|
newPart, err = be.DecryptName(part, zeroIV)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package nametransform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
60
internal/nametransform/pad16.go
Normal file
60
internal/nametransform/pad16.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package nametransform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"crypto/aes"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pad16 - pad data to AES block size (=16 byte) using standard PKCS#7 padding
|
||||||
|
// https://tools.ietf.org/html/rfc5652#section-6.3
|
||||||
|
func pad16(orig []byte) (padded []byte) {
|
||||||
|
oldLen := len(orig)
|
||||||
|
if oldLen == 0 {
|
||||||
|
panic("Padding zero-length string makes no sense")
|
||||||
|
}
|
||||||
|
padLen := aes.BlockSize - oldLen%aes.BlockSize
|
||||||
|
if padLen == 0 {
|
||||||
|
padLen = aes.BlockSize
|
||||||
|
}
|
||||||
|
newLen := oldLen + padLen
|
||||||
|
padded = make([]byte, newLen)
|
||||||
|
copy(padded, orig)
|
||||||
|
padByte := byte(padLen)
|
||||||
|
for i := oldLen; i < newLen; i++ {
|
||||||
|
padded[i] = padByte
|
||||||
|
}
|
||||||
|
return padded
|
||||||
|
}
|
||||||
|
|
||||||
|
// unPad16 - remove padding
|
||||||
|
func unPad16(padded []byte) ([]byte, error) {
|
||||||
|
oldLen := len(padded)
|
||||||
|
if oldLen%aes.BlockSize != 0 {
|
||||||
|
return nil, errors.New("Unaligned size")
|
||||||
|
}
|
||||||
|
// The last byte is always a padding byte
|
||||||
|
padByte := padded[oldLen-1]
|
||||||
|
// The padding byte's value is the padding length
|
||||||
|
padLen := int(padByte)
|
||||||
|
// Padding must be at least 1 byte
|
||||||
|
if padLen <= 0 {
|
||||||
|
return nil, errors.New("Padding cannot be zero-length")
|
||||||
|
}
|
||||||
|
// Larger paddings make no sense
|
||||||
|
if padLen > aes.BlockSize {
|
||||||
|
return nil, fmt.Errorf("Padding too long, padLen = %d > 16", padLen)
|
||||||
|
}
|
||||||
|
// All padding bytes must be identical
|
||||||
|
for i := oldLen - padLen; i < oldLen; i++ {
|
||||||
|
if padded[i] != padByte {
|
||||||
|
return nil, fmt.Errorf("Padding byte at i=%d is invalid", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newLen := oldLen - padLen
|
||||||
|
// Padding an empty string makes no sense
|
||||||
|
if newLen == 0 {
|
||||||
|
return nil, errors.New("Unpadded length is zero")
|
||||||
|
}
|
||||||
|
return padded[0:newLen], nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cryptfs
|
package toggledlog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -7,6 +7,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProgramName = "gocryptfs"
|
||||||
|
)
|
||||||
|
|
||||||
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 {
|
@ -1,7 +1,7 @@
|
|||||||
// +build !go1.5
|
// +build !go1.5
|
||||||
// = go 1.4 or lower
|
// = go 1.4 or lower
|
||||||
|
|
||||||
package cryptfs
|
package toggledlog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log/syslog"
|
"log/syslog"
|
@ -1,14 +1,14 @@
|
|||||||
// +build go1.5
|
// +build go1.5
|
||||||
// = go 1.5 or higher
|
// = go 1.5 or higher
|
||||||
|
|
||||||
package cryptfs
|
package toggledlog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l *toggledLogger) SwitchToSyslog(p syslog.Priority) {
|
func (l *toggledLogger) SwitchToSyslog(p syslog.Priority) {
|
||||||
w, err := syslog.New(p, PROGRAM_NAME)
|
w, err := syslog.New(p, ProgramName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Warn.Printf("Cannot switch 0x%02x to syslog: %v", p, err)
|
Warn.Printf("Cannot switch 0x%02x to syslog: %v", p, err)
|
||||||
} else {
|
} else {
|
106
main.go
106
main.go
@ -16,12 +16,16 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
|
||||||
"github.com/rfjakob/gocryptfs/pathfs_frontend"
|
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/fuse"
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/pathfs_frontend"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/contentenc"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -56,9 +60,9 @@ func initDir(args *argContainer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create gocryptfs.conf
|
// Create gocryptfs.conf
|
||||||
cryptfs.Info.Printf("Choose a password for protecting your files.")
|
toggledlog.Info.Printf("Choose a password for protecting your files.")
|
||||||
password := readPasswordTwice(args.extpass)
|
password := readPasswordTwice(args.extpass)
|
||||||
err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn)
|
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ERREXIT_INIT)
|
||||||
@ -66,30 +70,30 @@ func initDir(args *argContainer) {
|
|||||||
|
|
||||||
if args.diriv && !args.plaintextnames {
|
if args.diriv && !args.plaintextnames {
|
||||||
// Create gocryptfs.diriv in the root dir
|
// Create gocryptfs.diriv in the root dir
|
||||||
err = cryptfs.WriteDirIV(args.cipherdir)
|
err = nametransform.WriteDirIV(args.cipherdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ERREXIT_INIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptfs.Info.Printf(colorGreen + "The filesystem has been created successfully." + colorReset)
|
toggledlog.Info.Printf(colorGreen + "The filesystem has been created successfully." + colorReset)
|
||||||
cryptfs.Info.Printf(colorGrey+"You can now mount it using: %s %s MOUNTPOINT"+colorReset,
|
toggledlog.Info.Printf(colorGrey+"You can now mount it using: %s %s MOUNTPOINT"+colorReset,
|
||||||
cryptfs.PROGRAM_NAME, args.cipherdir)
|
toggledlog.ProgramName, args.cipherdir)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func usageText() {
|
func usageText() {
|
||||||
printVersion()
|
printVersion()
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
fmt.Printf("Usage: %s -init|-passwd [OPTIONS] CIPHERDIR\n", cryptfs.PROGRAM_NAME)
|
fmt.Printf("Usage: %s -init|-passwd [OPTIONS] CIPHERDIR\n", toggledlog.ProgramName)
|
||||||
fmt.Printf(" or %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", cryptfs.PROGRAM_NAME)
|
fmt.Printf(" or %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", toggledlog.ProgramName)
|
||||||
fmt.Printf("\nOptions:\n")
|
fmt.Printf("\nOptions:\n")
|
||||||
flagSet.PrintDefaults()
|
flagSet.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadConfig - load the config file "filename", prompting the user for the password
|
// loadConfig - load the config file "filename", prompting the user for the password
|
||||||
func loadConfig(args *argContainer) (masterkey []byte, confFile *cryptfs.ConfFile) {
|
func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.ConfFile) {
|
||||||
// Check if the file exists at all before prompting for a password
|
// Check if the file exists at all before prompting for a password
|
||||||
_, err := os.Stat(args.config)
|
_, err := os.Stat(args.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -100,16 +104,16 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *cryptfs.ConfFil
|
|||||||
fmt.Printf("Password: ")
|
fmt.Printf("Password: ")
|
||||||
}
|
}
|
||||||
pw := readPassword(args.extpass)
|
pw := readPassword(args.extpass)
|
||||||
cryptfs.Info.Printf("Decrypting master key... ")
|
toggledlog.Info.Printf("Decrypting master key... ")
|
||||||
cryptfs.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password
|
toggledlog.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password
|
||||||
masterkey, confFile, err = cryptfs.LoadConfFile(args.config, pw)
|
masterkey, confFile, err = configfile.LoadConfFile(args.config, pw)
|
||||||
cryptfs.Warn.Enabled = true
|
toggledlog.Warn.Enabled = true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
fmt.Println(colorRed + "Wrong password." + colorReset)
|
fmt.Println(colorRed + "Wrong password." + colorReset)
|
||||||
os.Exit(ERREXIT_LOADCONF)
|
os.Exit(ERREXIT_LOADCONF)
|
||||||
}
|
}
|
||||||
cryptfs.Info.Printf("done.")
|
toggledlog.Info.Printf("done.")
|
||||||
|
|
||||||
return masterkey, confFile
|
return masterkey, confFile
|
||||||
}
|
}
|
||||||
@ -125,14 +129,14 @@ func changePassword(args *argContainer) {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ERREXIT_INIT)
|
||||||
}
|
}
|
||||||
cryptfs.Info.Printf("Password changed.")
|
toggledlog.Info.Printf("Password changed.")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// printVersion - print a version string like
|
// printVersion - print a version string like
|
||||||
// "gocryptfs v0.3.1-31-g6736212-dirty; on-disk format 2"
|
// "gocryptfs v0.3.1-31-g6736212-dirty; on-disk format 2"
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
fmt.Printf("%s %s; on-disk format %d\n", cryptfs.PROGRAM_NAME, GitVersion, cryptfs.HEADER_CURRENT_VERSION)
|
fmt.Printf("%s %s; on-disk format %d\n", toggledlog.ProgramName, GitVersion, contentenc.CurrentVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -142,7 +146,7 @@ func main() {
|
|||||||
setupColors()
|
setupColors()
|
||||||
|
|
||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
flagSet = flag.NewFlagSet(cryptfs.PROGRAM_NAME, flag.ExitOnError)
|
flagSet = flag.NewFlagSet(toggledlog.ProgramName, flag.ExitOnError)
|
||||||
flagSet.Usage = usageText
|
flagSet.Usage = usageText
|
||||||
flagSet.BoolVar(&args.debug, "d", false, "")
|
flagSet.BoolVar(&args.debug, "d", false, "")
|
||||||
flagSet.BoolVar(&args.debug, "debug", false, "Enable debug output")
|
flagSet.BoolVar(&args.debug, "debug", false, "Enable debug output")
|
||||||
@ -168,7 +172,7 @@ func main() {
|
|||||||
flagSet.StringVar(&args.extpass, "extpass", "", "Use external program for the password prompt")
|
flagSet.StringVar(&args.extpass, "extpass", "", "Use external program for the password prompt")
|
||||||
flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
|
flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
|
||||||
"successful mount - used internally for daemonization")
|
"successful mount - used internally for daemonization")
|
||||||
flagSet.IntVar(&args.scryptn, "scryptn", cryptfs.SCRYPT_DEFAULT_LOGN, "scrypt cost parameter logN. "+
|
flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. "+
|
||||||
"Setting this to a lower value speeds up mounting but makes the password susceptible to brute-force attacks")
|
"Setting this to a lower value speeds up mounting but makes the password susceptible to brute-force attacks")
|
||||||
flagSet.Parse(os.Args[1:])
|
flagSet.Parse(os.Args[1:])
|
||||||
|
|
||||||
@ -182,12 +186,12 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
if args.debug {
|
if args.debug {
|
||||||
cryptfs.Debug.Enabled = true
|
toggledlog.Debug.Enabled = true
|
||||||
cryptfs.Debug.Printf("Debug output enabled")
|
toggledlog.Debug.Printf("Debug output enabled")
|
||||||
}
|
}
|
||||||
if args.wpanic {
|
if args.wpanic {
|
||||||
cryptfs.Warn.PanicAfter = true
|
toggledlog.Warn.PanicAfter = true
|
||||||
cryptfs.Debug.Printf("Panicing on warnings")
|
toggledlog.Debug.Printf("Panicing on warnings")
|
||||||
}
|
}
|
||||||
// Every operation below requires CIPHERDIR. Check that we have it.
|
// Every operation below requires CIPHERDIR. Check that we have it.
|
||||||
if flagSet.NArg() >= 1 {
|
if flagSet.NArg() >= 1 {
|
||||||
@ -203,7 +207,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
// "-q"
|
// "-q"
|
||||||
if args.quiet {
|
if args.quiet {
|
||||||
cryptfs.Info.Enabled = false
|
toggledlog.Info.Enabled = false
|
||||||
}
|
}
|
||||||
// "-config"
|
// "-config"
|
||||||
if args.config != "" {
|
if args.config != "" {
|
||||||
@ -211,13 +215,13 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf(colorRed+"Invalid \"-config\" setting: %v\n"+colorReset, err)
|
fmt.Printf(colorRed+"Invalid \"-config\" setting: %v\n"+colorReset, err)
|
||||||
}
|
}
|
||||||
cryptfs.Info.Printf("Using config file at custom location %s", args.config)
|
toggledlog.Info.Printf("Using config file at custom location %s", args.config)
|
||||||
} else {
|
} else {
|
||||||
args.config = filepath.Join(args.cipherdir, cryptfs.ConfDefaultName)
|
args.config = filepath.Join(args.cipherdir, configfile.ConfDefaultName)
|
||||||
}
|
}
|
||||||
// "-cpuprofile"
|
// "-cpuprofile"
|
||||||
if args.cpuprofile != "" {
|
if args.cpuprofile != "" {
|
||||||
cryptfs.Info.Printf("Writing CPU profile to %s", args.cpuprofile)
|
toggledlog.Info.Printf("Writing CPU profile to %s", args.cpuprofile)
|
||||||
f, err := os.Create(args.cpuprofile)
|
f, err := os.Create(args.cpuprofile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@ -228,7 +232,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
// "-memprofile"
|
// "-memprofile"
|
||||||
if args.memprofile != "" {
|
if args.memprofile != "" {
|
||||||
cryptfs.Info.Printf("Writing mem profile to %s", args.memprofile)
|
toggledlog.Info.Printf("Writing mem profile to %s", args.memprofile)
|
||||||
f, err := os.Create(args.memprofile)
|
f, err := os.Create(args.memprofile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@ -245,13 +249,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
// "-openssl"
|
// "-openssl"
|
||||||
if args.openssl == false {
|
if args.openssl == false {
|
||||||
cryptfs.Info.Printf("Openssl disabled")
|
toggledlog.Info.Printf("Openssl disabled")
|
||||||
}
|
}
|
||||||
// Operation flags: init, passwd or mount
|
// Operation flags: init, passwd or mount
|
||||||
// "-init"
|
// "-init"
|
||||||
if args.init {
|
if args.init {
|
||||||
if flagSet.NArg() > 1 {
|
if flagSet.NArg() > 1 {
|
||||||
fmt.Printf("Usage: %s -init [OPTIONS] CIPHERDIR\n", cryptfs.PROGRAM_NAME)
|
fmt.Printf("Usage: %s -init [OPTIONS] CIPHERDIR\n", toggledlog.ProgramName)
|
||||||
os.Exit(ERREXIT_USAGE)
|
os.Exit(ERREXIT_USAGE)
|
||||||
}
|
}
|
||||||
initDir(&args) // does not return
|
initDir(&args) // does not return
|
||||||
@ -259,7 +263,7 @@ func main() {
|
|||||||
// "-passwd"
|
// "-passwd"
|
||||||
if args.passwd {
|
if args.passwd {
|
||||||
if flagSet.NArg() > 1 {
|
if flagSet.NArg() > 1 {
|
||||||
fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", cryptfs.PROGRAM_NAME)
|
fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", toggledlog.ProgramName)
|
||||||
os.Exit(ERREXIT_USAGE)
|
os.Exit(ERREXIT_USAGE)
|
||||||
}
|
}
|
||||||
changePassword(&args) // does not return
|
changePassword(&args) // does not return
|
||||||
@ -282,34 +286,34 @@ func main() {
|
|||||||
}
|
}
|
||||||
// Get master key
|
// Get master key
|
||||||
var masterkey []byte
|
var masterkey []byte
|
||||||
var confFile *cryptfs.ConfFile
|
var confFile *configfile.ConfFile
|
||||||
if args.masterkey != "" {
|
if args.masterkey != "" {
|
||||||
// "-masterkey"
|
// "-masterkey"
|
||||||
cryptfs.Info.Printf("Using explicit master key.")
|
toggledlog.Info.Printf("Using explicit master key.")
|
||||||
masterkey = parseMasterKey(args.masterkey)
|
masterkey = parseMasterKey(args.masterkey)
|
||||||
cryptfs.Info.Printf("THE MASTER KEY IS VISIBLE VIA \"ps -auxwww\", ONLY USE THIS MODE FOR EMERGENCIES.")
|
toggledlog.Info.Printf("THE MASTER KEY IS VISIBLE VIA \"ps -auxwww\", ONLY USE THIS MODE FOR EMERGENCIES.")
|
||||||
} else if args.zerokey {
|
} else if args.zerokey {
|
||||||
// "-zerokey"
|
// "-zerokey"
|
||||||
cryptfs.Info.Printf("Using all-zero dummy master key.")
|
toggledlog.Info.Printf("Using all-zero dummy master key.")
|
||||||
cryptfs.Info.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.")
|
toggledlog.Info.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.")
|
||||||
masterkey = make([]byte, cryptfs.KEY_LEN)
|
masterkey = make([]byte, cryptocore.KeyLen)
|
||||||
} else {
|
} else {
|
||||||
// Load master key from config file
|
// Load master key from config file
|
||||||
masterkey, confFile = loadConfig(&args)
|
masterkey, confFile = loadConfig(&args)
|
||||||
printMasterKey(masterkey)
|
printMasterKey(masterkey)
|
||||||
}
|
}
|
||||||
// Initialize FUSE server
|
// Initialize FUSE server
|
||||||
cryptfs.Debug.Printf("cli args: %v", args)
|
toggledlog.Debug.Printf("cli args: %v", args)
|
||||||
srv := pathfsFrontend(masterkey, args, confFile)
|
srv := pathfsFrontend(masterkey, args, confFile)
|
||||||
cryptfs.Info.Println(colorGreen + "Filesystem mounted and ready." + colorReset)
|
toggledlog.Info.Println(colorGreen + "Filesystem mounted and ready." + colorReset)
|
||||||
// We are ready - send USR1 signal to our parent and switch to syslog
|
// We are ready - send USR1 signal to our parent and switch to syslog
|
||||||
if args.notifypid > 0 {
|
if args.notifypid > 0 {
|
||||||
sendUsr1(args.notifypid)
|
sendUsr1(args.notifypid)
|
||||||
|
|
||||||
if !args.nosyslog {
|
if !args.nosyslog {
|
||||||
cryptfs.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO)
|
toggledlog.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO)
|
||||||
cryptfs.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG)
|
toggledlog.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG)
|
||||||
cryptfs.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
|
toggledlog.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wait for SIGINT in the background and unmount ourselves if we get it.
|
// Wait for SIGINT in the background and unmount ourselves if we get it.
|
||||||
@ -322,7 +326,7 @@ func main() {
|
|||||||
|
|
||||||
// pathfsFrontend - initialize gocryptfs/pathfs_frontend
|
// pathfsFrontend - initialize gocryptfs/pathfs_frontend
|
||||||
// Calls os.Exit on errors
|
// Calls os.Exit on errors
|
||||||
func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *fuse.Server {
|
func pathfsFrontend(key []byte, args argContainer, confFile *configfile.ConfFile) *fuse.Server {
|
||||||
|
|
||||||
// Reconciliate CLI and config file arguments into a Args struct that is passed to the
|
// Reconciliate CLI and config file arguments into a Args struct that is passed to the
|
||||||
// filesystem implementation
|
// filesystem implementation
|
||||||
@ -338,10 +342,10 @@ func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *
|
|||||||
// confFile is nil when "-zerokey" or "-masterkey" was used
|
// confFile is nil when "-zerokey" or "-masterkey" was used
|
||||||
if confFile != nil {
|
if confFile != nil {
|
||||||
// Settings from the config file override command line args
|
// Settings from the config file override command line args
|
||||||
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(cryptfs.FlagPlaintextNames)
|
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
|
||||||
frontendArgs.DirIV = confFile.IsFeatureFlagSet(cryptfs.FlagDirIV)
|
frontendArgs.DirIV = confFile.IsFeatureFlagSet(configfile.FlagDirIV)
|
||||||
frontendArgs.EMENames = confFile.IsFeatureFlagSet(cryptfs.FlagEMENames)
|
frontendArgs.EMENames = confFile.IsFeatureFlagSet(configfile.FlagEMENames)
|
||||||
frontendArgs.GCMIV128 = confFile.IsFeatureFlagSet(cryptfs.FlagGCMIV128)
|
frontendArgs.GCMIV128 = confFile.IsFeatureFlagSet(configfile.FlagGCMIV128)
|
||||||
}
|
}
|
||||||
// EMENames implies DirIV, both on the command line and in the config file.
|
// EMENames implies DirIV, both on the command line and in the config file.
|
||||||
if frontendArgs.EMENames {
|
if frontendArgs.EMENames {
|
||||||
@ -353,7 +357,7 @@ func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *
|
|||||||
frontendArgs.EMENames = false
|
frontendArgs.EMENames = false
|
||||||
}
|
}
|
||||||
jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
|
jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
|
||||||
cryptfs.Debug.Printf("frontendArgs: %s", string(jsonBytes))
|
toggledlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))
|
||||||
|
|
||||||
finalFs := pathfs_frontend.NewFS(frontendArgs)
|
finalFs := pathfs_frontend.NewFS(frontendArgs)
|
||||||
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
|
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
|
||||||
@ -398,7 +402,7 @@ func handleSigint(srv *fuse.Server, mountpoint string) {
|
|||||||
err := srv.Unmount()
|
err := srv.Unmount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Print(err)
|
fmt.Print(err)
|
||||||
cryptfs.Info.Printf("Trying lazy unmount")
|
toggledlog.Info.Printf("Trying lazy unmount")
|
||||||
cmd := exec.Command("fusermount", "-u", "-z", mountpoint)
|
cmd := exec.Command("fusermount", "-u", "-z", mountpoint)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
10
masterkey.go
10
masterkey.go
@ -3,9 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// printMasterKey - remind the user that he should store the master key in
|
// printMasterKey - remind the user that he should store the master key in
|
||||||
@ -25,7 +27,7 @@ func printMasterKey(key []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptfs.Info.Printf(`
|
toggledlog.Info.Printf(`
|
||||||
Your master key is:
|
Your master key is:
|
||||||
|
|
||||||
%s
|
%s
|
||||||
@ -46,8 +48,8 @@ func parseMasterKey(masterkey string) []byte {
|
|||||||
fmt.Printf("Could not parse master key: %v\n", err)
|
fmt.Printf("Could not parse master key: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if len(key) != cryptfs.KEY_LEN {
|
if len(key) != cryptocore.KeyLen {
|
||||||
fmt.Printf("Master key has length %d but we require length %d\n", len(key), cryptfs.KEY_LEN)
|
fmt.Printf("Master key has length %d but we require length %d\n", len(key), cryptocore.KeyLen)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
|
@ -13,7 +13,9 @@ import (
|
|||||||
|
|
||||||
"github.com/hanwen/go-fuse/fuse"
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/contentenc"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// File - based on loopbackFile in go-fuse/fuse/nodefs/files.go
|
// File - based on loopbackFile in go-fuse/fuse/nodefs/files.go
|
||||||
@ -29,19 +31,19 @@ type file struct {
|
|||||||
// Was the file opened O_WRONLY?
|
// Was the file opened O_WRONLY?
|
||||||
writeOnly bool
|
writeOnly bool
|
||||||
|
|
||||||
// Parent CryptFS
|
// Content encryption helper
|
||||||
cfs *cryptfs.CryptFS
|
contentEnc *contentenc.ContentEnc
|
||||||
|
|
||||||
// Inode number
|
// Inode number
|
||||||
ino uint64
|
ino uint64
|
||||||
|
|
||||||
// File header
|
// File header
|
||||||
header *cryptfs.FileHeader
|
header *contentenc.FileHeader
|
||||||
|
|
||||||
forgotten bool
|
forgotten bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFile(fd *os.File, writeOnly bool, cfs *cryptfs.CryptFS) nodefs.File {
|
func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) nodefs.File {
|
||||||
var st syscall.Stat_t
|
var st syscall.Stat_t
|
||||||
syscall.Fstat(int(fd.Fd()), &st)
|
syscall.Fstat(int(fd.Fd()), &st)
|
||||||
wlock.register(st.Ino)
|
wlock.register(st.Ino)
|
||||||
@ -49,7 +51,7 @@ func NewFile(fd *os.File, writeOnly bool, cfs *cryptfs.CryptFS) nodefs.File {
|
|||||||
return &file{
|
return &file{
|
||||||
fd: fd,
|
fd: fd,
|
||||||
writeOnly: writeOnly,
|
writeOnly: writeOnly,
|
||||||
cfs: cfs,
|
contentEnc: contentEnc,
|
||||||
ino: st.Ino,
|
ino: st.Ino,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,12 +73,12 @@ 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, cryptfs.HEADER_LEN)
|
buf := make([]byte, contentenc.HEADER_LEN)
|
||||||
_, err := f.fd.ReadAt(buf, 0)
|
_, err := f.fd.ReadAt(buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
h, err := cryptfs.ParseHeader(buf)
|
h, err := contentenc.ParseHeader(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -87,13 +89,13 @@ func (f *file) readHeader() error {
|
|||||||
|
|
||||||
// createHeader - create a new random header and write it to disk
|
// createHeader - create a new random header and write it to disk
|
||||||
func (f *file) createHeader() error {
|
func (f *file) createHeader() error {
|
||||||
h := cryptfs.RandomHeader()
|
h := contentenc.RandomHeader()
|
||||||
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 := prealloc(int(f.fd.Fd()), 0, cryptfs.HEADER_LEN)
|
err := prealloc(int(f.fd.Fd()), 0, contentenc.HEADER_LEN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error())
|
toggledlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,29 +135,29 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the backing ciphertext in one go
|
// Read the backing ciphertext in one go
|
||||||
blocks := f.cfs.ExplodePlainRange(off, length)
|
blocks := f.contentEnc.ExplodePlainRange(off, length)
|
||||||
alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks)
|
alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks)
|
||||||
skip := blocks[0].Skip
|
skip := blocks[0].Skip
|
||||||
cryptfs.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d", off, length, alignedOffset, alignedLength, skip)
|
toggledlog.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d", off, length, alignedOffset, alignedLength, skip)
|
||||||
ciphertext := make([]byte, int(alignedLength))
|
ciphertext := make([]byte, int(alignedLength))
|
||||||
n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))
|
n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
cryptfs.Warn.Printf("read: ReadAt: %s", err.Error())
|
toggledlog.Warn.Printf("read: ReadAt: %s", err.Error())
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Truncate ciphertext buffer down to actually read bytes
|
// Truncate ciphertext buffer down to actually read bytes
|
||||||
ciphertext = ciphertext[0:n]
|
ciphertext = ciphertext[0:n]
|
||||||
|
|
||||||
firstBlockNo := blocks[0].BlockNo
|
firstBlockNo := blocks[0].BlockNo
|
||||||
cryptfs.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)
|
toggledlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)
|
||||||
|
|
||||||
// Decrypt it
|
// Decrypt it
|
||||||
plaintext, err := f.cfs.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.cfs.PlainOffToBlockNo(uint64(len(plaintext)))
|
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
|
||||||
cipherOff := f.cfs.BlockNoToCipherOff(curruptBlockNo)
|
cipherOff := f.contentEnc.BlockNoToCipherOff(curruptBlockNo)
|
||||||
plainOff := f.cfs.BlockNoToPlainOff(curruptBlockNo)
|
plainOff := f.contentEnc.BlockNoToPlainOff(curruptBlockNo)
|
||||||
cryptfs.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d, cipherOff=%d)",
|
toggledlog.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d, cipherOff=%d)",
|
||||||
f.ino, curruptBlockNo, plainOff, cipherOff)
|
f.ino, curruptBlockNo, plainOff, cipherOff)
|
||||||
return nil, fuse.EIO
|
return nil, fuse.EIO
|
||||||
}
|
}
|
||||||
@ -179,23 +181,23 @@ func (f *file) Read(buf []byte, off int64) (resultData fuse.ReadResult, code fus
|
|||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
|
|
||||||
cryptfs.Debug.Printf("ino%d: FUSE Read: offset=%d length=%d", f.ino, len(buf), off)
|
toggledlog.Debug.Printf("ino%d: FUSE Read: offset=%d length=%d", f.ino, len(buf), off)
|
||||||
|
|
||||||
if f.writeOnly {
|
if f.writeOnly {
|
||||||
cryptfs.Warn.Printf("ino%d: Tried to read from write-only file", f.ino)
|
toggledlog.Warn.Printf("ino%d: Tried to read from write-only file", f.ino)
|
||||||
return nil, fuse.EBADF
|
return nil, fuse.EBADF
|
||||||
}
|
}
|
||||||
|
|
||||||
out, status := f.doRead(uint64(off), uint64(len(buf)))
|
out, status := f.doRead(uint64(off), uint64(len(buf)))
|
||||||
|
|
||||||
if status == fuse.EIO {
|
if status == fuse.EIO {
|
||||||
cryptfs.Warn.Printf("ino%d: Read failed with EIO, offset=%d, length=%d", f.ino, len(buf), off)
|
toggledlog.Warn.Printf("ino%d: Read failed with EIO, offset=%d, length=%d", f.ino, len(buf), off)
|
||||||
}
|
}
|
||||||
if status != fuse.OK {
|
if status != fuse.OK {
|
||||||
return nil, status
|
return nil, status
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptfs.Debug.Printf("ino%d: Read: status %v, returning %d bytes", f.ino, status, len(out))
|
toggledlog.Debug.Printf("ino%d: Read: status %v, returning %d bytes", f.ino, status, len(out))
|
||||||
return fuse.ReadResultData(out), status
|
return fuse.ReadResultData(out), status
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +227,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
var written uint32
|
var written uint32
|
||||||
status := fuse.OK
|
status := fuse.OK
|
||||||
dataBuf := bytes.NewBuffer(data)
|
dataBuf := bytes.NewBuffer(data)
|
||||||
blocks := f.cfs.ExplodePlainRange(uint64(off), uint64(len(data)))
|
blocks := f.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data)))
|
||||||
for _, b := range blocks {
|
for _, b := range blocks {
|
||||||
|
|
||||||
blockData := dataBuf.Next(int(b.Length))
|
blockData := dataBuf.Next(int(b.Length))
|
||||||
@ -234,26 +236,26 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
if b.IsPartial() {
|
if b.IsPartial() {
|
||||||
// Read
|
// Read
|
||||||
o, _ := b.PlaintextRange()
|
o, _ := b.PlaintextRange()
|
||||||
oldData, status := f.doRead(o, f.cfs.PlainBS())
|
oldData, status := f.doRead(o, f.contentEnc.PlainBS())
|
||||||
if status != fuse.OK {
|
if status != fuse.OK {
|
||||||
cryptfs.Warn.Printf("ino%d fh%d: RMW read failed: %s", f.ino, f.intFd(), status.String())
|
toggledlog.Warn.Printf("ino%d fh%d: RMW read failed: %s", f.ino, f.intFd(), status.String())
|
||||||
return written, status
|
return written, status
|
||||||
}
|
}
|
||||||
// Modify
|
// Modify
|
||||||
blockData = f.cfs.MergeBlocks(oldData, blockData, int(b.Skip))
|
blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip))
|
||||||
cryptfs.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData))
|
toggledlog.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt
|
// Encrypt
|
||||||
blockOffset, blockLen := b.CiphertextRange()
|
blockOffset, blockLen := b.CiphertextRange()
|
||||||
blockData = f.cfs.EncryptBlock(blockData, b.BlockNo, f.header.Id)
|
blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.Id)
|
||||||
cryptfs.Debug.Printf("ino%d: Writing %d bytes to block #%d",
|
toggledlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",
|
||||||
f.ino, uint64(len(blockData))-f.cfs.BlockOverhead(), b.BlockNo)
|
f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo)
|
||||||
|
|
||||||
// Prevent partially written (=corrupt) blocks by preallocating the space beforehand
|
// Prevent partially written (=corrupt) blocks by preallocating the space beforehand
|
||||||
err := prealloc(int(f.fd.Fd()), int64(blockOffset), int64(blockLen))
|
err := prealloc(int(f.fd.Fd()), int64(blockOffset), int64(blockLen))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.ino, f.intFd(), err.Error())
|
toggledlog.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.ino, f.intFd(), err.Error())
|
||||||
status = fuse.ToStatus(err)
|
status = fuse.ToStatus(err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -262,7 +264,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
_, err = f.fd.WriteAt(blockData, int64(blockOffset))
|
_, err = f.fd.WriteAt(blockData, int64(blockOffset))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("doWrite: Write failed: %s", err.Error())
|
toggledlog.Warn.Printf("doWrite: Write failed: %s", err.Error())
|
||||||
status = fuse.ToStatus(err)
|
status = fuse.ToStatus(err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -278,18 +280,18 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {
|
|||||||
wlock.lock(f.ino)
|
wlock.lock(f.ino)
|
||||||
defer wlock.unlock(f.ino)
|
defer wlock.unlock(f.ino)
|
||||||
|
|
||||||
cryptfs.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.ino, off, len(data))
|
toggledlog.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.ino, off, len(data))
|
||||||
|
|
||||||
fi, err := f.fd.Stat()
|
fi, err := f.fd.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Write: Fstat failed: %v", err)
|
toggledlog.Warn.Printf("Write: Fstat failed: %v", err)
|
||||||
return 0, fuse.ToStatus(err)
|
return 0, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
plainSize := f.cfs.CipherSizeToPlainSize(uint64(fi.Size()))
|
plainSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))
|
||||||
if f.createsHole(plainSize, off) {
|
if f.createsHole(plainSize, off) {
|
||||||
status := f.zeroPad(plainSize)
|
status := f.zeroPad(plainSize)
|
||||||
if status != fuse.OK {
|
if status != fuse.OK {
|
||||||
cryptfs.Warn.Printf("zeroPad returned error %v", status)
|
toggledlog.Warn.Printf("zeroPad returned error %v", status)
|
||||||
return 0, status
|
return 0, status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,14 +339,14 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
defer wlock.unlock(f.ino)
|
defer wlock.unlock(f.ino)
|
||||||
|
|
||||||
if f.forgotten {
|
if f.forgotten {
|
||||||
cryptfs.Warn.Printf("ino%d fh%d: Truncate on forgotten file", f.ino, f.intFd())
|
toggledlog.Warn.Printf("ino%d fh%d: Truncate on forgotten file", f.ino, f.intFd())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common case first: Truncate to zero
|
// Common case first: Truncate to zero
|
||||||
if newSize == 0 {
|
if newSize == 0 {
|
||||||
err := syscall.Ftruncate(int(f.fd.Fd()), 0)
|
err := syscall.Ftruncate(int(f.fd.Fd()), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.ino, f.intFd(), err)
|
toggledlog.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.ino, f.intFd(), err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Truncate to zero kills the file header
|
// Truncate to zero kills the file header
|
||||||
@ -356,14 +358,14 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
// the file
|
// the file
|
||||||
fi, err := f.fd.Stat()
|
fi, err := f.fd.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("ino%d fh%d: Truncate: Fstat failed: %v", f.ino, f.intFd(), err)
|
toggledlog.Warn.Printf("ino%d fh%d: Truncate: Fstat failed: %v", f.ino, f.intFd(), err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
oldSize := f.cfs.CipherSizeToPlainSize(uint64(fi.Size()))
|
oldSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))
|
||||||
{
|
{
|
||||||
oldB := float32(oldSize) / float32(f.cfs.PlainBS())
|
oldB := float32(oldSize) / float32(f.contentEnc.PlainBS())
|
||||||
newB := float32(newSize) / float32(f.cfs.PlainBS())
|
newB := float32(newSize) / float32(f.contentEnc.PlainBS())
|
||||||
cryptfs.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize)
|
toggledlog.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
|
||||||
@ -382,7 +384,7 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks := f.cfs.ExplodePlainRange(oldSize, newSize-oldSize)
|
blocks := f.contentEnc.ExplodePlainRange(oldSize, newSize-oldSize)
|
||||||
for _, b := range blocks {
|
for _, b := range blocks {
|
||||||
// First and last block may be partial
|
// First and last block may be partial
|
||||||
if b.IsPartial() {
|
if b.IsPartial() {
|
||||||
@ -396,7 +398,7 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
off, length := b.CiphertextRange()
|
off, length := b.CiphertextRange()
|
||||||
err := syscall.Ftruncate(int(f.fd.Fd()), int64(off+length))
|
err := syscall.Ftruncate(int(f.fd.Fd()), int64(off+length))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("grow Ftruncate returned error: %v", err)
|
toggledlog.Warn.Printf("grow Ftruncate returned error: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,23 +406,23 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
|
|||||||
return fuse.OK
|
return fuse.OK
|
||||||
} else {
|
} else {
|
||||||
// File shrinks
|
// File shrinks
|
||||||
blockNo := f.cfs.PlainOffToBlockNo(newSize)
|
blockNo := f.contentEnc.PlainOffToBlockNo(newSize)
|
||||||
cipherOff := f.cfs.BlockNoToCipherOff(blockNo)
|
cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo)
|
||||||
plainOff := f.cfs.BlockNoToPlainOff(blockNo)
|
plainOff := f.contentEnc.BlockNoToPlainOff(blockNo)
|
||||||
lastBlockLen := newSize - plainOff
|
lastBlockLen := newSize - plainOff
|
||||||
var data []byte
|
var data []byte
|
||||||
if lastBlockLen > 0 {
|
if lastBlockLen > 0 {
|
||||||
var status fuse.Status
|
var status fuse.Status
|
||||||
data, status = f.doRead(plainOff, lastBlockLen)
|
data, status = f.doRead(plainOff, lastBlockLen)
|
||||||
if status != fuse.OK {
|
if status != fuse.OK {
|
||||||
cryptfs.Warn.Printf("shrink doRead returned error: %v", err)
|
toggledlog.Warn.Printf("shrink doRead returned error: %v", err)
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Truncate down to last complete block
|
// Truncate down to last complete block
|
||||||
err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff))
|
err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("shrink Ftruncate returned error: %v", err)
|
toggledlog.Warn.Printf("shrink Ftruncate returned error: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Append partial block
|
// Append partial block
|
||||||
@ -450,14 +452,14 @@ func (f *file) GetAttr(a *fuse.Attr) fuse.Status {
|
|||||||
f.fdLock.RLock()
|
f.fdLock.RLock()
|
||||||
defer f.fdLock.RUnlock()
|
defer f.fdLock.RUnlock()
|
||||||
|
|
||||||
cryptfs.Debug.Printf("file.GetAttr()")
|
toggledlog.Debug.Printf("file.GetAttr()")
|
||||||
st := syscall.Stat_t{}
|
st := syscall.Stat_t{}
|
||||||
err := syscall.Fstat(int(f.fd.Fd()), &st)
|
err := syscall.Fstat(int(f.fd.Fd()), &st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
a.FromStat(&st)
|
a.FromStat(&st)
|
||||||
a.Size = f.cfs.CipherSizeToPlainSize(a.Size)
|
a.Size = f.contentEnc.CipherSizeToPlainSize(a.Size)
|
||||||
|
|
||||||
return fuse.OK
|
return fuse.OK
|
||||||
}
|
}
|
||||||
@ -468,7 +470,7 @@ var allocateWarned bool
|
|||||||
func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {
|
||||||
// Only warn once
|
// Only warn once
|
||||||
if !allocateWarned {
|
if !allocateWarned {
|
||||||
cryptfs.Warn.Printf("fallocate(2) is not supported, returning ENOSYS - see https://github.com/rfjakob/gocryptfs/issues/1")
|
toggledlog.Warn.Printf("fallocate(2) is not supported, returning ENOSYS - see https://github.com/rfjakob/gocryptfs/issues/1")
|
||||||
allocateWarned = true
|
allocateWarned = true
|
||||||
}
|
}
|
||||||
return fuse.ENOSYS
|
return fuse.ENOSYS
|
||||||
|
@ -4,13 +4,14 @@ package pathfs_frontend
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hanwen/go-fuse/fuse"
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Will a write to offset "off" create a file hole?
|
// Will a write to offset "off" create a file hole?
|
||||||
func (f *file) createsHole(plainSize uint64, off int64) bool {
|
func (f *file) createsHole(plainSize uint64, off int64) bool {
|
||||||
nextBlock := f.cfs.PlainOffToBlockNo(plainSize)
|
nextBlock := f.contentEnc.PlainOffToBlockNo(plainSize)
|
||||||
targetBlock := f.cfs.PlainOffToBlockNo(uint64(off))
|
targetBlock := f.contentEnc.PlainOffToBlockNo(uint64(off))
|
||||||
if targetBlock > nextBlock {
|
if targetBlock > nextBlock {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -19,10 +20,10 @@ func (f *file) createsHole(plainSize uint64, off int64) bool {
|
|||||||
|
|
||||||
// Zero-pad the file of size plainSize to the next block boundary
|
// Zero-pad the file of size plainSize to the next block boundary
|
||||||
func (f *file) zeroPad(plainSize uint64) fuse.Status {
|
func (f *file) zeroPad(plainSize uint64) fuse.Status {
|
||||||
lastBlockLen := plainSize % f.cfs.PlainBS()
|
lastBlockLen := plainSize % f.contentEnc.PlainBS()
|
||||||
missing := f.cfs.PlainBS() - lastBlockLen
|
missing := f.contentEnc.PlainBS() - lastBlockLen
|
||||||
pad := make([]byte, missing)
|
pad := make([]byte, missing)
|
||||||
cryptfs.Debug.Printf("zeroPad: Writing %d bytes\n", missing)
|
toggledlog.Debug.Printf("zeroPad: Writing %d bytes\n", missing)
|
||||||
_, status := f.doWrite(pad, int64(plainSize))
|
_, status := f.doWrite(pad, int64(plainSize))
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
@ -13,25 +13,41 @@ import (
|
|||||||
"github.com/hanwen/go-fuse/fuse"
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
"github.com/hanwen/go-fuse/fuse/nodefs"
|
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||||
"github.com/hanwen/go-fuse/fuse/pathfs"
|
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/contentenc"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const plainBS = 4096
|
||||||
|
|
||||||
type FS struct {
|
type FS struct {
|
||||||
*cryptfs.CryptFS
|
|
||||||
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
|
||||||
// dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified
|
// dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified
|
||||||
// Readers must RLock() it to prevent them from seeing intermediate
|
// Readers must RLock() it to prevent them from seeing intermediate
|
||||||
// states
|
// states
|
||||||
dirIVLock sync.RWMutex
|
dirIVLock sync.RWMutex
|
||||||
|
// Filename encryption helper
|
||||||
|
nameTransform *nametransform.NameTransform
|
||||||
|
// Content encryption helper
|
||||||
|
contentEnc *contentenc.ContentEnc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypted FUSE overlay filesystem
|
// Encrypted FUSE overlay filesystem
|
||||||
func NewFS(args Args) *FS {
|
func NewFS(args Args) *FS {
|
||||||
|
|
||||||
|
cryptoCore := cryptocore.New(args.Masterkey, args.OpenSSL, args.GCMIV128)
|
||||||
|
contentEnc := contentenc.New(cryptoCore, plainBS)
|
||||||
|
nameTransform := nametransform.New(cryptoCore, args.EMENames)
|
||||||
|
|
||||||
return &FS{
|
return &FS{
|
||||||
CryptFS: cryptfs.NewCryptFS(args.Masterkey, args.OpenSSL, args.PlaintextNames, args.GCMIV128),
|
|
||||||
FileSystem: pathfs.NewLoopbackFileSystem(args.Cipherdir),
|
FileSystem: pathfs.NewLoopbackFileSystem(args.Cipherdir),
|
||||||
args: args,
|
args: args,
|
||||||
|
nameTransform: nameTransform,
|
||||||
|
contentEnc: contentEnc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,12 +59,12 @@ func (fs *FS) getBackingPath(relPath string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
cAbsPath := filepath.Join(fs.args.Cipherdir, cPath)
|
cAbsPath := filepath.Join(fs.args.Cipherdir, cPath)
|
||||||
cryptfs.Debug.Printf("getBackingPath: %s + %s -> %s", fs.args.Cipherdir, relPath, cAbsPath)
|
toggledlog.Debug.Printf("getBackingPath: %s + %s -> %s", fs.args.Cipherdir, relPath, cAbsPath)
|
||||||
return cAbsPath, nil
|
return cAbsPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
cryptfs.Debug.Printf("FS.GetAttr('%s')", name)
|
toggledlog.Debug.Printf("FS.GetAttr('%s')", name)
|
||||||
if fs.isFiltered(name) {
|
if fs.isFiltered(name) {
|
||||||
return nil, fuse.EPERM
|
return nil, fuse.EPERM
|
||||||
}
|
}
|
||||||
@ -58,11 +74,11 @@ func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Stat
|
|||||||
}
|
}
|
||||||
a, status := fs.FileSystem.GetAttr(cName, context)
|
a, status := fs.FileSystem.GetAttr(cName, context)
|
||||||
if a == nil {
|
if a == nil {
|
||||||
cryptfs.Debug.Printf("FS.GetAttr failed: %s", status.String())
|
toggledlog.Debug.Printf("FS.GetAttr failed: %s", status.String())
|
||||||
return a, status
|
return a, status
|
||||||
}
|
}
|
||||||
if a.IsRegular() {
|
if a.IsRegular() {
|
||||||
a.Size = fs.CipherSizeToPlainSize(a.Size)
|
a.Size = fs.contentEnc.CipherSizeToPlainSize(a.Size)
|
||||||
} else if a.IsSymlink() {
|
} else if a.IsSymlink() {
|
||||||
target, _ := fs.Readlink(name, context)
|
target, _ := fs.Readlink(name, context)
|
||||||
a.Size = uint64(len(target))
|
a.Size = uint64(len(target))
|
||||||
@ -71,7 +87,7 @@ func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Stat
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
cryptfs.Debug.Printf("OpenDir(%s)", dirName)
|
toggledlog.Debug.Printf("OpenDir(%s)", dirName)
|
||||||
cDirName, err := fs.encryptPath(dirName)
|
cDirName, err := fs.encryptPath(dirName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
@ -81,12 +97,12 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
|
|||||||
if cipherEntries == nil {
|
if cipherEntries == nil {
|
||||||
return nil, status
|
return nil, status
|
||||||
}
|
}
|
||||||
// Get DirIV (stays zero if DirIV if off)
|
// Get DirIV (stays nil if DirIV if off)
|
||||||
cachedIV := make([]byte, cryptfs.DIRIV_LEN)
|
var cachedIV []byte
|
||||||
if fs.args.DirIV {
|
if fs.args.DirIV {
|
||||||
// Read the DirIV once and use it for all later name decryptions
|
// Read the DirIV once and use it for all later name decryptions
|
||||||
cDirAbsPath := filepath.Join(fs.args.Cipherdir, cDirName)
|
cDirAbsPath := filepath.Join(fs.args.Cipherdir, cDirName)
|
||||||
cachedIV, err = fs.CryptFS.ReadDirIV(cDirAbsPath)
|
cachedIV, err = fs.nameTransform.ReadDirIV(cDirAbsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
@ -95,19 +111,19 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
|
|||||||
var plain []fuse.DirEntry
|
var plain []fuse.DirEntry
|
||||||
for i := range cipherEntries {
|
for i := range cipherEntries {
|
||||||
cName := cipherEntries[i].Name
|
cName := cipherEntries[i].Name
|
||||||
if dirName == "" && cName == cryptfs.ConfDefaultName {
|
if dirName == "" && cName == configfile.ConfDefaultName {
|
||||||
// silently ignore "gocryptfs.conf" in the top level dir
|
// silently ignore "gocryptfs.conf" in the top level dir
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if fs.args.DirIV && cName == cryptfs.DIRIV_FILENAME {
|
if fs.args.DirIV && cName == nametransform.DirIVFilename {
|
||||||
// silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled
|
// silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var name string = cName
|
var name string = cName
|
||||||
if !fs.args.PlaintextNames {
|
if !fs.args.PlaintextNames {
|
||||||
name, err = fs.CryptFS.DecryptName(cName, cachedIV, fs.args.EMENames)
|
name, err = fs.nameTransform.DecryptName(cName, cachedIV)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Invalid name \"%s\" in dir \"%s\": %s", cName, cDirName, err)
|
toggledlog.Warn.Printf("Invalid name \"%s\" in dir \"%s\": %s", cName, cDirName, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,16 +153,16 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n
|
|||||||
iflags, writeOnly := fs.mangleOpenFlags(flags)
|
iflags, writeOnly := fs.mangleOpenFlags(flags)
|
||||||
cPath, err := fs.getBackingPath(path)
|
cPath, err := fs.getBackingPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Debug.Printf("Open: getBackingPath: %v", err)
|
toggledlog.Debug.Printf("Open: getBackingPath: %v", err)
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
cryptfs.Debug.Printf("Open: %s", cPath)
|
toggledlog.Debug.Printf("Open: %s", cPath)
|
||||||
f, err := os.OpenFile(cPath, iflags, 0666)
|
f, err := os.OpenFile(cPath, iflags, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewFile(f, writeOnly, fs.CryptFS), fuse.OK
|
return NewFile(f, writeOnly, fs.contentEnc), fuse.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -162,7 +178,7 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fuse.ToStatus(err)
|
return nil, fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
return NewFile(f, writeOnly, fs.CryptFS), fuse.OK
|
return NewFile(f, writeOnly, fs.contentEnc), fuse.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -203,7 +219,7 @@ var truncateWarned bool
|
|||||||
func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
||||||
// Only warn once
|
// Only warn once
|
||||||
if !truncateWarned {
|
if !truncateWarned {
|
||||||
cryptfs.Warn.Printf("truncate(2) is not supported, returning ENOSYS - use ftruncate(2)")
|
toggledlog.Warn.Printf("truncate(2) is not supported, returning ENOSYS - use ftruncate(2)")
|
||||||
truncateWarned = true
|
truncateWarned = true
|
||||||
}
|
}
|
||||||
return fuse.ENOSYS
|
return fuse.ENOSYS
|
||||||
@ -233,7 +249,7 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f
|
|||||||
if !fs.args.DirIV {
|
if !fs.args.DirIV {
|
||||||
target, err := fs.decryptPath(cTarget)
|
target, err := fs.decryptPath(cTarget)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Readlink: CBC decryption failed: %v", err)
|
toggledlog.Warn.Printf("Readlink: CBC decryption failed: %v", err)
|
||||||
return "", fuse.EIO
|
return "", fuse.EIO
|
||||||
}
|
}
|
||||||
return target, fuse.OK
|
return target, fuse.OK
|
||||||
@ -241,12 +257,12 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f
|
|||||||
// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM)
|
// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM)
|
||||||
cBinTarget, err := base64.URLEncoding.DecodeString(cTarget)
|
cBinTarget, err := base64.URLEncoding.DecodeString(cTarget)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Readlink: %v", err)
|
toggledlog.Warn.Printf("Readlink: %v", err)
|
||||||
return "", fuse.EIO
|
return "", fuse.EIO
|
||||||
}
|
}
|
||||||
target, err := fs.CryptFS.DecryptBlock([]byte(cBinTarget), 0, nil)
|
target, err := fs.contentEnc.DecryptBlock([]byte(cBinTarget), 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Readlink: %v", err)
|
toggledlog.Warn.Printf("Readlink: %v", err)
|
||||||
return "", fuse.EIO
|
return "", fuse.EIO
|
||||||
}
|
}
|
||||||
return string(target), fuse.OK
|
return string(target), fuse.OK
|
||||||
@ -264,7 +280,7 @@ func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
|
toggledlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
|
||||||
if fs.isFiltered(linkName) {
|
if fs.isFiltered(linkName) {
|
||||||
return fuse.EPERM
|
return fuse.EPERM
|
||||||
}
|
}
|
||||||
@ -276,18 +292,18 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co
|
|||||||
if !fs.args.DirIV {
|
if !fs.args.DirIV {
|
||||||
cTarget, err := fs.encryptPath(target)
|
cTarget, err := fs.encryptPath(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Symlink: BUG: we should not get an error here: %v", err)
|
toggledlog.Warn.Printf("Symlink: BUG: we should not get an error here: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
err = os.Symlink(cTarget, cPath)
|
err = os.Symlink(cTarget, cPath)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM)
|
// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM)
|
||||||
cBinTarget := fs.CryptFS.EncryptBlock([]byte(target), 0, nil)
|
cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil)
|
||||||
cTarget := base64.URLEncoding.EncodeToString(cBinTarget)
|
cTarget := base64.URLEncoding.EncodeToString(cBinTarget)
|
||||||
|
|
||||||
err = os.Symlink(cTarget, cPath)
|
err = os.Symlink(cTarget, cPath)
|
||||||
cryptfs.Debug.Printf("Symlink: os.Symlink(%s, %s) = %v", cTarget, cPath, err)
|
toggledlog.Debug.Printf("Symlink: os.Symlink(%s, %s) = %v", cTarget, cPath, err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +321,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
|||||||
}
|
}
|
||||||
// The Rename may cause a directory to take the place of another directory.
|
// The Rename may cause a directory to take the place of another directory.
|
||||||
// That directory may still be in the DirIV cache, clear it.
|
// That directory may still be in the DirIV cache, clear it.
|
||||||
fs.CryptFS.DirIVCache.Clear()
|
fs.nameTransform.DirIVCache.Clear()
|
||||||
|
|
||||||
err = os.Rename(cOldPath, cNewPath)
|
err = os.Rename(cOldPath, cNewPath)
|
||||||
|
|
||||||
@ -313,7 +329,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
|||||||
// If an empty directory is overwritten we will always get
|
// If an empty directory is overwritten we will always get
|
||||||
// ENOTEMPTY as the "empty" directory will still contain gocryptfs.diriv.
|
// ENOTEMPTY as the "empty" directory will still contain gocryptfs.diriv.
|
||||||
// Handle that case by removing the target directory and trying again.
|
// Handle that case by removing the target directory and trying again.
|
||||||
cryptfs.Debug.Printf("Rename: Handling ENOTEMPTY")
|
toggledlog.Debug.Printf("Rename: Handling ENOTEMPTY")
|
||||||
if fs.Rmdir(newPath, context) == fuse.OK {
|
if fs.Rmdir(newPath, context) == fuse.OK {
|
||||||
err = os.Rename(cOldPath, cNewPath)
|
err = os.Rename(cOldPath, cNewPath)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/fuse"
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
||||||
@ -29,7 +32,7 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu
|
|||||||
mode = mode | 0300
|
mode = mode | 0300
|
||||||
|
|
||||||
// The new directory may take the place of an older one that is still in the cache
|
// The new directory may take the place of an older one that is still in the cache
|
||||||
fs.CryptFS.DirIVCache.Clear()
|
fs.nameTransform.DirIVCache.Clear()
|
||||||
// Create directory
|
// Create directory
|
||||||
fs.dirIVLock.Lock()
|
fs.dirIVLock.Lock()
|
||||||
defer fs.dirIVLock.Unlock()
|
defer fs.dirIVLock.Unlock()
|
||||||
@ -38,13 +41,13 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu
|
|||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Create gocryptfs.diriv inside
|
// Create gocryptfs.diriv inside
|
||||||
err = cryptfs.WriteDirIV(encPath)
|
err = nametransform.WriteDirIV(encPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This should not happen
|
// This should not happen
|
||||||
cryptfs.Warn.Printf("Mkdir: WriteDirIV failed: %v", err)
|
toggledlog.Warn.Printf("Mkdir: WriteDirIV failed: %v", err)
|
||||||
err2 := syscall.Rmdir(encPath)
|
err2 := syscall.Rmdir(encPath)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
cryptfs.Warn.Printf("Mkdir: Rmdir rollback failed: %v", err2)
|
toggledlog.Warn.Printf("Mkdir: Rmdir rollback failed: %v", err2)
|
||||||
}
|
}
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
@ -53,7 +56,7 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu
|
|||||||
if origMode != mode {
|
if origMode != mode {
|
||||||
err = os.Chmod(encPath, os.FileMode(origMode))
|
err = os.Chmod(encPath, os.FileMode(origMode))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Mkdir: Chmod failed: %v", err)
|
toggledlog.Warn.Printf("Mkdir: Chmod failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,17 +77,17 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
|||||||
fd, err := os.Open(encPath)
|
fd, err := os.Open(encPath)
|
||||||
if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.EACCES {
|
if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.EACCES {
|
||||||
// We need permission to read and modify the directory
|
// We need permission to read and modify the directory
|
||||||
cryptfs.Debug.Printf("Rmdir: handling EACCESS")
|
toggledlog.Debug.Printf("Rmdir: handling EACCESS")
|
||||||
fi, err2 := os.Stat(encPath)
|
fi, err2 := os.Stat(encPath)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
cryptfs.Debug.Printf("Rmdir: Stat: %v", err2)
|
toggledlog.Debug.Printf("Rmdir: Stat: %v", err2)
|
||||||
return fuse.ToStatus(err2)
|
return fuse.ToStatus(err2)
|
||||||
}
|
}
|
||||||
origMode := fi.Mode()
|
origMode := fi.Mode()
|
||||||
newMode := origMode | 0700
|
newMode := origMode | 0700
|
||||||
err2 = os.Chmod(encPath, newMode)
|
err2 = os.Chmod(encPath, newMode)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
cryptfs.Debug.Printf("Rmdir: Chmod failed: %v", err2)
|
toggledlog.Debug.Printf("Rmdir: Chmod failed: %v", err2)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -92,7 +95,7 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
|||||||
// Undo the chmod if removing the directory failed
|
// Undo the chmod if removing the directory failed
|
||||||
err3 := os.Chmod(encPath, origMode)
|
err3 := os.Chmod(encPath, origMode)
|
||||||
if err3 != nil {
|
if err3 != nil {
|
||||||
cryptfs.Warn.Printf("Rmdir: Chmod rollback failed: %v", err2)
|
toggledlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -100,35 +103,36 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
|||||||
fd, err = os.Open(encPath)
|
fd, err = os.Open(encPath)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Debug.Printf("Rmdir: Open: %v", err)
|
toggledlog.Debug.Printf("Rmdir: Open: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
list, err := fd.Readdirnames(10)
|
list, err := fd.Readdirnames(10)
|
||||||
fd.Close()
|
fd.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Debug.Printf("Rmdir: Readdirnames: %v", err)
|
toggledlog.Debug.Printf("Rmdir: Readdirnames: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
if len(list) > 1 {
|
if len(list) > 1 {
|
||||||
return fuse.ToStatus(syscall.ENOTEMPTY)
|
return fuse.ToStatus(syscall.ENOTEMPTY)
|
||||||
} else if len(list) == 0 {
|
} else if len(list) == 0 {
|
||||||
cryptfs.Warn.Printf("Rmdir: gocryptfs.diriv missing, allowing deletion")
|
toggledlog.Warn.Printf("Rmdir: gocryptfs.diriv missing, allowing deletion")
|
||||||
return fuse.ToStatus(syscall.Rmdir(encPath))
|
return fuse.ToStatus(syscall.Rmdir(encPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ"
|
// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ"
|
||||||
dirivPath := filepath.Join(encPath, cryptfs.DIRIV_FILENAME)
|
dirivPath := filepath.Join(encPath, nametransform.DirIVFilename)
|
||||||
parentDir := filepath.Dir(encPath)
|
parentDir := filepath.Dir(encPath)
|
||||||
tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptfs.RandUint64())
|
tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64())
|
||||||
tmpDirivPath := filepath.Join(parentDir, tmpName)
|
tmpDirivPath := filepath.Join(parentDir, tmpName)
|
||||||
cryptfs.Debug.Printf("Rmdir: Renaming %s to %s", cryptfs.DIRIV_FILENAME, tmpDirivPath)
|
toggledlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpDirivPath)
|
||||||
// The directory is in an inconsistent state between rename and rmdir. Protect against
|
// The directory is in an inconsistent state between rename and rmdir. Protect against
|
||||||
// concurrent readers.
|
// concurrent readers.
|
||||||
fs.dirIVLock.Lock()
|
fs.dirIVLock.Lock()
|
||||||
defer fs.dirIVLock.Unlock()
|
defer fs.dirIVLock.Unlock()
|
||||||
err = os.Rename(dirivPath, tmpDirivPath)
|
err = os.Rename(dirivPath, tmpDirivPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Rmdir: Renaming %s to %s failed: %v", cryptfs.DIRIV_FILENAME, tmpDirivPath, err)
|
toggledlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
|
||||||
|
nametransform.DirIVFilename, tmpDirivPath, err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Actual Rmdir
|
// Actual Rmdir
|
||||||
@ -138,16 +142,16 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
|||||||
// meantime, undo the rename
|
// meantime, undo the rename
|
||||||
err2 := os.Rename(tmpDirivPath, dirivPath)
|
err2 := os.Rename(tmpDirivPath, dirivPath)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
cryptfs.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
|
toggledlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
|
||||||
}
|
}
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Delete "gocryptfs.diriv.rmdir.INODENUMBER"
|
// Delete "gocryptfs.diriv.rmdir.INODENUMBER"
|
||||||
err = syscall.Unlink(tmpDirivPath)
|
err = syscall.Unlink(tmpDirivPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cryptfs.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
|
toggledlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
|
||||||
}
|
}
|
||||||
// The now-deleted directory may have been in the DirIV cache. Clear it.
|
// The now-deleted directory may have been in the DirIV cache. Clear it.
|
||||||
fs.CryptFS.DirIVCache.Clear()
|
fs.nameTransform.DirIVCache.Clear()
|
||||||
return fuse.OK
|
return fuse.OK
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ package pathfs_frontend
|
|||||||
// This file forwards file encryption operations to cryptfs
|
// This file forwards file encryption operations to cryptfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/rfjakob/gocryptfs/cryptfs"
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
||||||
|
mylog "github.com/rfjakob/gocryptfs/internal/toggledlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// isFiltered - check if plaintext "path" should be forbidden
|
// isFiltered - check if plaintext "path" should be forbidden
|
||||||
@ -14,9 +15,9 @@ func (fs *FS) isFiltered(path string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// gocryptfs.conf in the root directory is forbidden
|
// gocryptfs.conf in the root directory is forbidden
|
||||||
if path == cryptfs.ConfDefaultName {
|
if path == configfile.ConfDefaultName {
|
||||||
cryptfs.Info.Printf("The name /%s is reserved when -plaintextnames is used\n",
|
mylog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n",
|
||||||
cryptfs.ConfDefaultName)
|
configfile.ConfDefaultName)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames
|
// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames
|
||||||
@ -30,11 +31,11 @@ func (fs *FS) encryptPath(plainPath string) (string, error) {
|
|||||||
return plainPath, nil
|
return plainPath, nil
|
||||||
}
|
}
|
||||||
if !fs.args.DirIV {
|
if !fs.args.DirIV {
|
||||||
return fs.CryptFS.EncryptPathNoIV(plainPath), nil
|
return fs.nameTransform.EncryptPathNoIV(plainPath), nil
|
||||||
}
|
}
|
||||||
fs.dirIVLock.RLock()
|
fs.dirIVLock.RLock()
|
||||||
defer fs.dirIVLock.RUnlock()
|
defer fs.dirIVLock.RUnlock()
|
||||||
return fs.CryptFS.EncryptPathDirIV(plainPath, fs.args.Cipherdir, fs.args.EMENames)
|
return fs.nameTransform.EncryptPathDirIV(plainPath, fs.args.Cipherdir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// decryptPath - decrypt relative ciphertext path
|
// decryptPath - decrypt relative ciphertext path
|
||||||
@ -43,9 +44,9 @@ func (fs *FS) decryptPath(cipherPath string) (string, error) {
|
|||||||
return cipherPath, nil
|
return cipherPath, nil
|
||||||
}
|
}
|
||||||
if !fs.args.DirIV {
|
if !fs.args.DirIV {
|
||||||
return fs.CryptFS.DecryptPathNoIV(cipherPath)
|
return fs.nameTransform.DecryptPathNoIV(cipherPath)
|
||||||
}
|
}
|
||||||
fs.dirIVLock.RLock()
|
fs.dirIVLock.RLock()
|
||||||
defer fs.dirIVLock.RUnlock()
|
defer fs.dirIVLock.RUnlock()
|
||||||
return fs.CryptFS.DecryptPathDirIV(cipherPath, fs.args.Cipherdir, fs.args.EMENames)
|
return fs.nameTransform.DecryptPathDirIV(cipherPath, fs.args.Cipherdir, fs.args.EMENames)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user