summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cryptfs/config_file.go104
-rw-r--r--cryptfs/cryptfs.go1
-rw-r--r--cryptfs/nonce.go9
-rw-r--r--main.go33
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
diff --git a/main.go b/main.go
index 70f0b53..d0aa33f 100644
--- a/main.go
+++ b/main.go
@@ -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)
}
}