From 7e92ebe16a7735b29e0fdc62d4b5d49ce0dc2b66 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Thu, 16 Jun 2016 19:02:47 +0200 Subject: Rename nametransform, contentenc source files Let's have shorter names, and merge *_api.go into the "main" file. No code changes. --- internal/contentenc/content.go | 33 ++++++++ internal/contentenc/content_api.go | 35 --------- internal/nametransform/diriv.go | 143 ++++++++++++++++++++++++++++++++++ internal/nametransform/name_api.go | 18 ----- internal/nametransform/names.go | 84 ++++++++++++++++++++ internal/nametransform/names_core.go | 65 ---------------- internal/nametransform/names_diriv.go | 143 ---------------------------------- internal/nametransform/names_noiv.go | 63 --------------- internal/nametransform/noiv.go | 63 +++++++++++++++ 9 files changed, 323 insertions(+), 324 deletions(-) delete mode 100644 internal/contentenc/content_api.go create mode 100644 internal/nametransform/diriv.go delete mode 100644 internal/nametransform/name_api.go create mode 100644 internal/nametransform/names.go delete mode 100644 internal/nametransform/names_core.go delete mode 100644 internal/nametransform/names_diriv.go delete mode 100644 internal/nametransform/names_noiv.go create mode 100644 internal/nametransform/noiv.go (limited to 'internal') diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index 5bac2a2..2298c5e 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -8,9 +8,42 @@ import ( "encoding/hex" "errors" + "github.com/rfjakob/gocryptfs/internal/cryptocore" "github.com/rfjakob/gocryptfs/internal/tlog" ) +const ( + // Default plaintext block size + DefaultBS = 4096 +) + +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 +} + // DecryptBlocks - Decrypt a number of blocks func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId []byte) ([]byte, error) { cBuf := bytes.NewBuffer(ciphertext) diff --git a/internal/contentenc/content_api.go b/internal/contentenc/content_api.go deleted file mode 100644 index cf482b6..0000000 --- a/internal/contentenc/content_api.go +++ /dev/null @@ -1,35 +0,0 @@ -package contentenc - -import "github.com/rfjakob/gocryptfs/internal/cryptocore" - -const ( - // Default plaintext block size - DefaultBS = 4096 -) - -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 -} diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go new file mode 100644 index 0000000..b9473aa --- /dev/null +++ b/internal/nametransform/diriv.go @@ -0,0 +1,143 @@ +package nametransform + +import ( + "errors" + "io/ioutil" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/rfjakob/gocryptfs/internal/cryptocore" + "github.com/rfjakob/gocryptfs/internal/tlog" +) + +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" +) + +// ReadDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path) +// This function is exported because it allows for an efficient readdir implementation. +func ReadDirIV(dir string) (iv []byte, err error) { + dirfd, err := os.Open(dir) + if err != nil { + return nil, err + } + defer dirfd.Close() + + return ReadDirIVAt(dirfd) +} + +// ReadDirIVAt reads "gocryptfs.diriv" from the directory that is opened as "dirfd". +// Using the dirfd makes it immune to concurrent renames of the directory. +func ReadDirIVAt(dirfd *os.File) (iv []byte, err error) { + fdRaw, err := syscall.Openat(int(dirfd.Fd()), DirIVFilename, syscall.O_RDONLY, 0) + if err != nil { + tlog.Warn.Printf("ReadDirIVAt: opening %q in dir %q failed: %v", + DirIVFilename, dirfd.Name(), err) + return nil, err + } + fd := os.NewFile(uintptr(fdRaw), DirIVFilename) + defer fd.Close() + + iv = make([]byte, dirIVLen+1) + n, err := fd.Read(iv) + if err != nil { + tlog.Warn.Printf("ReadDirIVAt: Read failed: %v", err) + return nil, err + } + iv = iv[0:n] + if len(iv) != dirIVLen { + tlog.Warn.Printf("ReadDirIVAt: wanted %d bytes, got %d", dirIVLen, len(iv)) + return nil, errors.New("invalid iv length") + } + return iv, nil +} + +// WriteDirIV - create diriv file inside "dir" (absolute ciphertext path) +// This function is exported because it is used from pathfs_frontend, main, +// and also the automated tests. +func WriteDirIV(dir string) error { + iv := cryptocore.RandBytes(dirIVLen) + file := filepath.Join(dir, DirIVFilename) + err := ioutil.WriteFile(file, iv, 0400) + if err != nil { + tlog.Warn.Printf("WriteDirIV: %v", err) + } + return err +} + +// EncryptPathDirIV - encrypt relative plaintext path using EME with DirIV. +// Components that are longer than 255 bytes are hashed if be.longnames == true. +func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cipherPath string, err error) { + // Empty string means root directory + if plainPath == "" { + return plainPath, nil + } + // Reject names longer than 255 bytes already here. This relieves everybody + // who uses hashed long names from checking for that later. + baseName := filepath.Base(plainPath) + if len(baseName) > syscall.NAME_MAX { + return "", syscall.ENAMETOOLONG + } + // Check if the DirIV is cached + parentDir := filepath.Dir(plainPath) + found, iv, cParentDir := be.DirIVCache.lookup(parentDir) + if found { + cBaseName := be.EncryptName(baseName, iv) + if be.longNames && len(cBaseName) > syscall.NAME_MAX { + cBaseName = HashLongName(cBaseName) + } + cipherPath = cParentDir + "/" + cBaseName + return cipherPath, nil + } + // Not cached - walk the directory tree + var wd = rootDir + var encryptedNames []string + plainNames := strings.Split(plainPath, "/") + for _, plainName := range plainNames { + iv, err = ReadDirIV(wd) + if err != nil { + return "", err + } + encryptedName := be.EncryptName(plainName, iv) + if be.longNames && len(encryptedName) > syscall.NAME_MAX { + encryptedName = HashLongName(encryptedName) + } + encryptedNames = append(encryptedNames, encryptedName) + wd = filepath.Join(wd, encryptedName) + } + cipherPath = strings.Join(encryptedNames, "/") + // Cache the final DirIV + cParentDir = filepath.Dir(cipherPath) + be.DirIVCache.store(parentDir, iv, cParentDir) + return cipherPath, nil +} + +// DecryptPathDirIV - decrypt path using EME with DirIV +// +// TODO This has only a single user, Readlink(), and only for compatability with +// gocryptfs v0.5. Drop? +func (be *NameTransform) DecryptPathDirIV(encryptedPath string, rootDir string) (string, error) { + var wd = rootDir + var plainNames []string + encryptedNames := strings.Split(encryptedPath, "/") + tlog.Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames) + for _, encryptedName := range encryptedNames { + iv, err := ReadDirIV(wd) + if err != nil { + return "", err + } + plainName, err := be.DecryptName(encryptedName, iv) + if err != nil { + return "", err + } + plainNames = append(plainNames, plainName) + wd = filepath.Join(wd, encryptedName) + } + return filepath.Join(plainNames...), nil +} diff --git a/internal/nametransform/name_api.go b/internal/nametransform/name_api.go deleted file mode 100644 index 7ac7d26..0000000 --- a/internal/nametransform/name_api.go +++ /dev/null @@ -1,18 +0,0 @@ -package nametransform - -import "github.com/rfjakob/gocryptfs/internal/cryptocore" - -type NameTransform struct { - cryptoCore *cryptocore.CryptoCore - useEME bool - longNames bool - DirIVCache dirIVCache -} - -func New(c *cryptocore.CryptoCore, useEME bool, longNames bool) *NameTransform { - return &NameTransform{ - cryptoCore: c, - longNames: longNames, - useEME: useEME, - } -} diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go new file mode 100644 index 0000000..8a7e260 --- /dev/null +++ b/internal/nametransform/names.go @@ -0,0 +1,84 @@ +package nametransform + +// Filename encryption / decryption functions + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "fmt" + + "github.com/rfjakob/eme" + + "github.com/rfjakob/gocryptfs/internal/cryptocore" +) + +type NameTransform struct { + cryptoCore *cryptocore.CryptoCore + useEME bool + longNames bool + DirIVCache dirIVCache +} + +func New(c *cryptocore.CryptoCore, useEME bool, longNames bool) *NameTransform { + return &NameTransform{ + cryptoCore: c, + longNames: longNames, + useEME: useEME, + } +} + +// DecryptName - decrypt base64-encoded encrypted filename "cipherName" +// Used by DecryptPathDirIV(). +// The 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". +// Used internally by EncryptPathDirIV(). +// The encryption is either CBC or EME, depending on "useEME". +// +// This function is exported because fusefrontend needs access to the full (not hashed) +// name if longname is used. Otherwise you should use EncryptPathDirIV() +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 +} diff --git a/internal/nametransform/names_core.go b/internal/nametransform/names_core.go deleted file mode 100644 index 779b885..0000000 --- a/internal/nametransform/names_core.go +++ /dev/null @@ -1,65 +0,0 @@ -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". -// -// This function is exported because fusefrontend needs access to the full (not hashed) -// name if longname is used -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 -} diff --git a/internal/nametransform/names_diriv.go b/internal/nametransform/names_diriv.go deleted file mode 100644 index b9473aa..0000000 --- a/internal/nametransform/names_diriv.go +++ /dev/null @@ -1,143 +0,0 @@ -package nametransform - -import ( - "errors" - "io/ioutil" - "os" - "path/filepath" - "strings" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -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" -) - -// ReadDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path) -// This function is exported because it allows for an efficient readdir implementation. -func ReadDirIV(dir string) (iv []byte, err error) { - dirfd, err := os.Open(dir) - if err != nil { - return nil, err - } - defer dirfd.Close() - - return ReadDirIVAt(dirfd) -} - -// ReadDirIVAt reads "gocryptfs.diriv" from the directory that is opened as "dirfd". -// Using the dirfd makes it immune to concurrent renames of the directory. -func ReadDirIVAt(dirfd *os.File) (iv []byte, err error) { - fdRaw, err := syscall.Openat(int(dirfd.Fd()), DirIVFilename, syscall.O_RDONLY, 0) - if err != nil { - tlog.Warn.Printf("ReadDirIVAt: opening %q in dir %q failed: %v", - DirIVFilename, dirfd.Name(), err) - return nil, err - } - fd := os.NewFile(uintptr(fdRaw), DirIVFilename) - defer fd.Close() - - iv = make([]byte, dirIVLen+1) - n, err := fd.Read(iv) - if err != nil { - tlog.Warn.Printf("ReadDirIVAt: Read failed: %v", err) - return nil, err - } - iv = iv[0:n] - if len(iv) != dirIVLen { - tlog.Warn.Printf("ReadDirIVAt: wanted %d bytes, got %d", dirIVLen, len(iv)) - return nil, errors.New("invalid iv length") - } - return iv, nil -} - -// WriteDirIV - create diriv file inside "dir" (absolute ciphertext path) -// This function is exported because it is used from pathfs_frontend, main, -// and also the automated tests. -func WriteDirIV(dir string) error { - iv := cryptocore.RandBytes(dirIVLen) - file := filepath.Join(dir, DirIVFilename) - err := ioutil.WriteFile(file, iv, 0400) - if err != nil { - tlog.Warn.Printf("WriteDirIV: %v", err) - } - return err -} - -// EncryptPathDirIV - encrypt relative plaintext path using EME with DirIV. -// Components that are longer than 255 bytes are hashed if be.longnames == true. -func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cipherPath string, err error) { - // Empty string means root directory - if plainPath == "" { - return plainPath, nil - } - // Reject names longer than 255 bytes already here. This relieves everybody - // who uses hashed long names from checking for that later. - baseName := filepath.Base(plainPath) - if len(baseName) > syscall.NAME_MAX { - return "", syscall.ENAMETOOLONG - } - // Check if the DirIV is cached - parentDir := filepath.Dir(plainPath) - found, iv, cParentDir := be.DirIVCache.lookup(parentDir) - if found { - cBaseName := be.EncryptName(baseName, iv) - if be.longNames && len(cBaseName) > syscall.NAME_MAX { - cBaseName = HashLongName(cBaseName) - } - cipherPath = cParentDir + "/" + cBaseName - return cipherPath, nil - } - // Not cached - walk the directory tree - var wd = rootDir - var encryptedNames []string - plainNames := strings.Split(plainPath, "/") - for _, plainName := range plainNames { - iv, err = ReadDirIV(wd) - if err != nil { - return "", err - } - encryptedName := be.EncryptName(plainName, iv) - if be.longNames && len(encryptedName) > syscall.NAME_MAX { - encryptedName = HashLongName(encryptedName) - } - encryptedNames = append(encryptedNames, encryptedName) - wd = filepath.Join(wd, encryptedName) - } - cipherPath = strings.Join(encryptedNames, "/") - // Cache the final DirIV - cParentDir = filepath.Dir(cipherPath) - be.DirIVCache.store(parentDir, iv, cParentDir) - return cipherPath, nil -} - -// DecryptPathDirIV - decrypt path using EME with DirIV -// -// TODO This has only a single user, Readlink(), and only for compatability with -// gocryptfs v0.5. Drop? -func (be *NameTransform) DecryptPathDirIV(encryptedPath string, rootDir string) (string, error) { - var wd = rootDir - var plainNames []string - encryptedNames := strings.Split(encryptedPath, "/") - tlog.Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames) - for _, encryptedName := range encryptedNames { - iv, err := ReadDirIV(wd) - if err != nil { - return "", err - } - plainName, err := be.DecryptName(encryptedName, iv) - if err != nil { - return "", err - } - plainNames = append(plainNames, plainName) - wd = filepath.Join(wd, encryptedName) - } - return filepath.Join(plainNames...), nil -} diff --git a/internal/nametransform/names_noiv.go b/internal/nametransform/names_noiv.go deleted file mode 100644 index f1009e4..0000000 --- a/internal/nametransform/names_noiv.go +++ /dev/null @@ -1,63 +0,0 @@ -package nametransform - -import ( - "strings" -) - -const ( - OpEncrypt = iota - OpDecrypt -) - -// DecryptPathNoIV - decrypt path using CBC without any IV. -// This function is deprecated by the the more secure DirIV variant and only retained -// for compatability with old filesystems. -func (be *NameTransform) DecryptPathNoIV(cipherPath string) (plainPath string, err error) { - plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt) - return plainPath, err -} - -// EncryptPathNoIV - decrypt path using CBC without any IV. -// This function is deprecated by the the more secure DirIV variant and only retained -// for compatability with old filesystems. -func (be *NameTransform) EncryptPathNoIV(plainPath string) (cipherPath string) { - cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt) - return cipherPath -} - -// translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV. -// Just splits the string on "/" and hands the parts to encryptName() / decryptName() -func (be *NameTransform) translatePathNoIV(path string, op int) (string, error) { - var err error - - // Empty string means root directory - if path == "" { - return path, err - } - - zeroIV := make([]byte, dirIVLen) - - // Run operation on each path component - var translatedParts []string - parts := strings.Split(path, "/") - for _, part := range parts { - if part == "" { - // This happens on "/foo/bar/" on the front and on the end. - // Don't panic. - translatedParts = append(translatedParts, "") - continue - } - var newPart string - if op == OpEncrypt { - newPart = be.EncryptName(part, zeroIV) - } else { - newPart, err = be.DecryptName(part, zeroIV) - if err != nil { - return "", err - } - } - translatedParts = append(translatedParts, newPart) - } - - return strings.Join(translatedParts, "/"), err -} diff --git a/internal/nametransform/noiv.go b/internal/nametransform/noiv.go new file mode 100644 index 0000000..f1009e4 --- /dev/null +++ b/internal/nametransform/noiv.go @@ -0,0 +1,63 @@ +package nametransform + +import ( + "strings" +) + +const ( + OpEncrypt = iota + OpDecrypt +) + +// DecryptPathNoIV - decrypt path using CBC without any IV. +// This function is deprecated by the the more secure DirIV variant and only retained +// for compatability with old filesystems. +func (be *NameTransform) DecryptPathNoIV(cipherPath string) (plainPath string, err error) { + plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt) + return plainPath, err +} + +// EncryptPathNoIV - decrypt path using CBC without any IV. +// This function is deprecated by the the more secure DirIV variant and only retained +// for compatability with old filesystems. +func (be *NameTransform) EncryptPathNoIV(plainPath string) (cipherPath string) { + cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt) + return cipherPath +} + +// translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV. +// Just splits the string on "/" and hands the parts to encryptName() / decryptName() +func (be *NameTransform) translatePathNoIV(path string, op int) (string, error) { + var err error + + // Empty string means root directory + if path == "" { + return path, err + } + + zeroIV := make([]byte, dirIVLen) + + // Run operation on each path component + var translatedParts []string + parts := strings.Split(path, "/") + for _, part := range parts { + if part == "" { + // This happens on "/foo/bar/" on the front and on the end. + // Don't panic. + translatedParts = append(translatedParts, "") + continue + } + var newPart string + if op == OpEncrypt { + newPart = be.EncryptName(part, zeroIV) + } else { + newPart, err = be.DecryptName(part, zeroIV) + if err != nil { + return "", err + } + } + translatedParts = append(translatedParts, newPart) + } + + return strings.Join(translatedParts, "/"), err +} -- cgit v1.2.3