diff options
| -rw-r--r-- | cryptfs/config_file.go | 104 | ||||
| -rw-r--r-- | cryptfs/cryptfs.go | 1 | ||||
| -rw-r--r-- | cryptfs/nonce.go | 9 | ||||
| -rw-r--r-- | main.go | 33 | 
4 files changed, 139 insertions, 8 deletions
diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go new file mode 100644 index 0000000..752ed09 --- /dev/null +++ b/cryptfs/config_file.go @@ -0,0 +1,104 @@ +package cryptfs + +import ( +	"errors" +	"io/ioutil" +	"encoding/json" +) +import "os" + +const ( +	// Changing this string breaks backward compatability +	testBlockData = "gocryptfs test block" + +	// The dot "." is not used in base64url (RFC4648), hence +	// we can never clash with an encrypted file. +	ConfDefaultName = "gocryptfs.conf" +) + +type confFile struct { +	// File the config is saved in. Lowercase => not exported to JSON. +	filename string +	// Unencrypted AES key +	Key [16]byte +	// GCM ciphertext with auth tag to verify the key is correct +	TestBlock []byte +} + +// CreateConfFile - create a new config file with "key" and write to "filename" +func CreateConfFile(filename string, key [16]byte) error { +	var cf confFile +	cf.filename = filename +	cf.Key = key + +	// Generate test block +	cfs := NewCryptFS(cf.Key, false) +	cf.TestBlock = cfs.EncryptBlock([]byte(testBlockData)) + +	// Write file to disk +	err := cf.WriteFile() + +	return err +} + +// LoadConfFile - read config file from disk and verify the key using the +// embedded TestBlock +func LoadConfFile(filename string) (*confFile, error) { +	// Read from disk +	js, err := ioutil.ReadFile(filename) +	if err != nil { +		return nil, err +	} +	var cf confFile +	err = json.Unmarshal(js, &cf) +	if err != nil { +		return nil, err +	} +	cf.filename = filename + +	// Try to decrypt the test block to see if the key is correct +	// +	// Speed does not matter here. Use built-in crypto. +	cfs := NewCryptFS(cf.Key, false) +	d, err := cfs.DecryptBlock(cf.TestBlock) +	if err != nil { +		return nil, err +	} +	ds := string(d) +	if ds != testBlockData { +		return nil, errors.New("Invalid test block content: " + ds) +	} +	return &cf, nil +} + +// WriteFile - write out config in JSON format to file "filename.tmp" +// then rename over "filename" +func (cf *confFile) WriteFile() error { +	tmp := cf.filename + ".tmp" +	fd, err := os.Create(tmp) +	if err != nil { +		return err +	} +	js, err := json.Marshal(cf) +	if err != nil { +		return err +	} +	_, err = fd.Write(js) +	if err != nil { +		return err +	} +	err = fd.Sync() +	if err != nil { +		return err +	} +	err = fd.Close() +	if err != nil { +		return err +	} +	err = os.Rename(tmp, cf.filename) +	if err != nil { +		return err +	} + +	return nil +} diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go index 40a9024..8927d74 100644 --- a/cryptfs/cryptfs.go +++ b/cryptfs/cryptfs.go @@ -8,6 +8,7 @@ import (  )  const ( +	KEY_LEN = 16  	NONCE_LEN = 12  	AUTH_TAG_LEN = 16  	DEFAULT_PLAINBS = 4096 diff --git a/cryptfs/nonce.go b/cryptfs/nonce.go index f93f59c..3e464a3 100644 --- a/cryptfs/nonce.go +++ b/cryptfs/nonce.go @@ -16,8 +16,9 @@ type nonce96 struct {  var gcmNonce nonce96 -func (n *nonce96) randBytes(len int) []byte { -	b := make([]byte, len) +// Get "n" random bytes from /dev/urandom or panic +func RandBytes(n int) []byte { +	b := make([]byte, n)  	_, err := rand.Read(b)  	if err != nil {  		panic("Could not get random bytes for nonce") @@ -26,9 +27,9 @@ func (n *nonce96) randBytes(len int) []byte {  }  func (n *nonce96) init() { -	b := n.randBytes(8) +	b := RandBytes(8)  	n.low64 = binary.BigEndian.Uint64(b) -	b = n.randBytes(4) +	b = RandBytes(4)  	n.high32 = binary.BigEndian.Uint32(b)  	n.ready = 1  	return @@ -33,19 +33,39 @@ const (  	ERREXIT_SERVE  = 4  	ERREXIT_MOUNT2 = 5  	ERREXIT_CIPHERDIR = 6 +	ERREXIT_INIT = 7 +	ERREXIT_LOADCONF = 8  )  func main() {  	// Parse command line arguments  	var debug bool +	var init bool  	flag.BoolVar(&debug, "debug", false, "Enable debug output") +	flag.BoolVar(&init, "init", false, "Initialize encrypted directory")  	flag.Parse()  	if debug {  		cryptfs.Debug.Enable()  		cryptfs.Debug.Printf("Debug output enabled\n")  	} +	if init { +		if flag.NArg() != 1 { +			fmt.Printf("usage: %s --init CIPHERDIR\n", PROGRAM_NAME) +			os.Exit(ERREXIT_USAGE) +		} +		dir, _ := filepath.Abs(flag.Arg(0)) +		filename := filepath.Join(dir, cryptfs.ConfDefaultName) +		r := cryptfs.RandBytes(cryptfs.KEY_LEN) +		var key [16]byte +		copy(key[:], r) +		err := cryptfs.CreateConfFile(filename, key) +		if err != nil { +			fmt.Println(err) +			os.Exit(ERREXIT_INIT) +		} +		os.Exit(0) +	}  	if flag.NArg() < 2 { -		fmt.Printf("NArg=%d\n", flag.NArg())  		fmt.Printf("usage: %s CIPHERDIR MOUNTPOINT\n", PROGRAM_NAME)  		os.Exit(ERREXIT_USAGE)  	} @@ -59,12 +79,17 @@ func main() {  		os.Exit(ERREXIT_CIPHERDIR)  	} -	var key [16]byte +	cfname := filepath.Join(cipherdir, cryptfs.ConfDefaultName) +	cf, err := cryptfs.LoadConfFile(cfname) +	if err != nil { +		fmt.Println(err) +		os.Exit(ERREXIT_LOADCONF) +	}  	if USE_CLUEFS { -		cluefsFrontend(key, cipherdir, mountpoint) +		cluefsFrontend(cf.Key, cipherdir, mountpoint)  	} else { -		pathfsFrontend(key, cipherdir, mountpoint, debug) +		pathfsFrontend(cf.Key, cipherdir, mountpoint, debug)  	}  }  | 
