libgocryptfs: update to gocryptfs v2.1
This commit is contained in:
commit
f0e45c7b7e
@ -60,7 +60,7 @@ func gcf_list_dir(sessionID int, dirName string) (*C.char, *C.int, C.int) {
|
||||
var cachedIV []byte
|
||||
if !OpenedVolumes[sessionID].plainTextNames {
|
||||
// Read the DirIV from disk
|
||||
cachedIV, err = nametransform.ReadDirIVAt(fd)
|
||||
cachedIV, err = volume.nameTransform.ReadDirIVAt(fd)
|
||||
if err != nil {
|
||||
return nil, nil, 0
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func (volume *Volume) openBackingDir(relPath string) (dirfd int, cName string, e
|
||||
// Walk the directory tree
|
||||
parts := strings.Split(relPath, "/")
|
||||
for i, name := range parts {
|
||||
iv, err := nametransform.ReadDirIVAt(dirfd)
|
||||
iv, err := volume.nameTransform.ReadDirIVAt(dirfd)
|
||||
if err != nil {
|
||||
syscall.Close(dirfd)
|
||||
return -1, "", err
|
||||
@ -112,7 +112,7 @@ func (volume *Volume) prepareAtSyscall(path string) (dirfd int, cName string, er
|
||||
// Cache store
|
||||
if !volume.plainTextNames {
|
||||
// TODO: openBackingDir already calls ReadDirIVAt(). Avoid duplicate work?
|
||||
iv, err := nametransform.ReadDirIVAt(dirfd)
|
||||
iv, err := volume.nameTransform.ReadDirIVAt(dirfd)
|
||||
if err != nil {
|
||||
syscall.Close(dirfd)
|
||||
return -1, "", err
|
||||
|
@ -5,9 +5,7 @@ package configfile
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"syscall"
|
||||
|
||||
"os"
|
||||
@ -60,64 +58,66 @@ type ConfFile struct {
|
||||
filename string
|
||||
}
|
||||
|
||||
// randBytesDevRandom gets "n" random bytes from /dev/random or panics
|
||||
func randBytesDevRandom(n int) []byte {
|
||||
f, err := os.Open("/dev/random")
|
||||
if err != nil {
|
||||
log.Panic("Failed to open /dev/random: " + err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
b := make([]byte, n)
|
||||
_, err = io.ReadFull(f, b)
|
||||
if err != nil {
|
||||
log.Panic("Failed to read random bytes: " + err.Error())
|
||||
}
|
||||
return b
|
||||
// CreateArgs exists because the argument list to Create became too long.
|
||||
type CreateArgs struct {
|
||||
Filename string
|
||||
Password []byte
|
||||
PlaintextNames bool
|
||||
LogN int
|
||||
Creator string
|
||||
AESSIV bool
|
||||
DeterministicNames bool
|
||||
XChaCha20Poly1305 bool
|
||||
}
|
||||
|
||||
// Create - create a new config with a random key encrypted with
|
||||
// "password" and write it to "filename".
|
||||
// Uses scrypt with cost parameter logN.
|
||||
func Create(filename string, password []byte, plaintextNames bool,
|
||||
logN int, creator string, aessiv bool, devrandom bool, fido2CredentialID []byte, fido2HmacSalt []byte) error {
|
||||
var cf ConfFile
|
||||
cf.filename = filename
|
||||
cf.Creator = creator
|
||||
cf.Version = contentenc.CurrentVersion
|
||||
|
||||
// Set feature flags
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128])
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagHKDF])
|
||||
if plaintextNames {
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])
|
||||
// "Password" and write it to "Filename".
|
||||
// Uses scrypt with cost parameter "LogN".
|
||||
func Create(args *CreateArgs) error {
|
||||
cf := ConfFile{
|
||||
filename: args.Filename,
|
||||
Creator: args.Creator,
|
||||
Version: contentenc.CurrentVersion,
|
||||
}
|
||||
// Feature flags
|
||||
cf.setFeatureFlag(FlagHKDF)
|
||||
if args.XChaCha20Poly1305 {
|
||||
cf.setFeatureFlag(FlagXChaCha20Poly1305)
|
||||
} else {
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagDirIV])
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagRaw64])
|
||||
// 128-bit IVs are mandatory for AES-GCM (default is 96!) and AES-SIV,
|
||||
// XChaCha20Poly1305 uses even an even longer IV of 192 bits.
|
||||
cf.setFeatureFlag(FlagGCMIV128)
|
||||
}
|
||||
if aessiv {
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])
|
||||
}
|
||||
if len(fido2CredentialID) > 0 {
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagFIDO2])
|
||||
cf.FIDO2 = &FIDO2Params{
|
||||
CredentialID: fido2CredentialID,
|
||||
HMACSalt: fido2HmacSalt,
|
||||
if args.PlaintextNames {
|
||||
cf.setFeatureFlag(FlagPlaintextNames)
|
||||
} else {
|
||||
if !args.DeterministicNames {
|
||||
cf.setFeatureFlag(FlagDirIV)
|
||||
}
|
||||
cf.setFeatureFlag(FlagEMENames)
|
||||
cf.setFeatureFlag(FlagLongNames)
|
||||
cf.setFeatureFlag(FlagRaw64)
|
||||
}
|
||||
if args.AESSIV {
|
||||
cf.setFeatureFlag(FlagAESSIV)
|
||||
}
|
||||
// Catch bugs and invalid cli flag combinations early
|
||||
cf.ScryptObject = NewScryptKDF(args.LogN)
|
||||
if err := cf.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Catch bugs and invalid cli flag combinations early
|
||||
cf.ScryptObject = NewScryptKDF(args.LogN)
|
||||
if err := cf.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
{
|
||||
// Generate new random master key
|
||||
var key []byte
|
||||
if devrandom {
|
||||
key = randBytesDevRandom(cryptocore.KeyLen)
|
||||
} else {
|
||||
key = cryptocore.RandBytes(cryptocore.KeyLen)
|
||||
}
|
||||
key := cryptocore.RandBytes(cryptocore.KeyLen)
|
||||
// Encrypt it using the password
|
||||
// This sets ScryptObject and EncryptedKey
|
||||
// Note: this looks at the FeatureFlags, so call it AFTER setting them.
|
||||
cf.EncryptKey(key, password, logN, false)
|
||||
cf.EncryptKey(key, args.Password, args.LogN, false)
|
||||
for i := range key {
|
||||
key[i] = 0
|
||||
}
|
||||
@ -175,39 +175,22 @@ func Load(filename string) (*ConfFile, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cf.Version != contentenc.CurrentVersion {
|
||||
return nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version)
|
||||
}
|
||||
|
||||
// Check that all set feature flags are known
|
||||
for _, flag := range cf.FeatureFlags {
|
||||
if !cf.isFeatureFlagKnown(flag) {
|
||||
return nil, fmt.Errorf("Unsupported feature flag %q", flag)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all required feature flags are set
|
||||
var requiredFlags []flagIota
|
||||
if cf.IsFeatureFlagSet(FlagPlaintextNames) {
|
||||
requiredFlags = requiredFlagsPlaintextNames
|
||||
} else {
|
||||
requiredFlags = requiredFlagsNormal
|
||||
}
|
||||
deprecatedFs := false
|
||||
for _, i := range requiredFlags {
|
||||
if !cf.IsFeatureFlagSet(i) {
|
||||
fmt.Fprintf(os.Stderr, "Required feature flag %q is missing\n", knownFlags[i])
|
||||
deprecatedFs = true
|
||||
}
|
||||
}
|
||||
if deprecatedFs {
|
||||
return nil, exitcodes.NewErr("Deprecated filesystem", exitcodes.DeprecatedFS)
|
||||
if err := cf.Validate(); err != nil {
|
||||
return nil, exitcodes.NewErr(err.Error(), exitcodes.DeprecatedFS)
|
||||
}
|
||||
|
||||
// All good
|
||||
return &cf, nil
|
||||
}
|
||||
|
||||
func (cf *ConfFile) setFeatureFlag(flag flagIota) {
|
||||
if cf.IsFeatureFlagSet(flag) {
|
||||
// Already set, ignore
|
||||
return
|
||||
}
|
||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[flag])
|
||||
}
|
||||
|
||||
// DecryptMasterKey decrypts the masterkey stored in cf.EncryptedKey using
|
||||
// password.
|
||||
func (cf *ConfFile) DecryptMasterKey(password []byte, giveHash bool) (masterkey, scryptHash []byte, err error) {
|
||||
@ -296,6 +279,9 @@ func (cf *ConfFile) GetMasterkey(password, givenScryptHash, returnedScryptHashBu
|
||||
// then rename over "filename".
|
||||
// This way a password change atomically replaces the file.
|
||||
func (cf *ConfFile) WriteFile() error {
|
||||
if err := cf.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
tmp := cf.filename + ".tmp"
|
||||
// 0400 permissions: gocryptfs.conf should be kept secret and never be written to.
|
||||
fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400)
|
||||
@ -315,7 +301,7 @@ func (cf *ConfFile) WriteFile() error {
|
||||
err = fd.Sync()
|
||||
if err != nil {
|
||||
// This can happen on network drives: FRITZ.NAS mounted on MacOS returns
|
||||
// "operation not supported": https://github.com/rfjakob/gocryptfs/issues/390
|
||||
// "operation not supported": https://github.com/rfjakob/gocryptfs/v2/issues/390
|
||||
// Try sync instead
|
||||
syscall.Sync()
|
||||
}
|
||||
@ -340,3 +326,18 @@ func getKeyEncrypter(scryptHash []byte, useHKDF bool) *contentenc.ContentEnc {
|
||||
ce := contentenc.New(cc, 4096, false)
|
||||
return ce
|
||||
}
|
||||
|
||||
// ContentEncryption tells us which content encryption algorithm is selected
|
||||
func (cf *ConfFile) ContentEncryption() (algo cryptocore.AEADTypeEnum, err error) {
|
||||
if err := cf.Validate(); err != nil {
|
||||
return cryptocore.AEADTypeEnum{}, err
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) {
|
||||
return cryptocore.BackendXChaCha20Poly1305, nil
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagAESSIV) {
|
||||
return cryptocore.BackendAESSIV, nil
|
||||
}
|
||||
// If neither AES-SIV nor XChaCha are selected, we must be using AES-GCM
|
||||
return cryptocore.BackendGoGCM, nil
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ const (
|
||||
// This flag is mandatory since gocryptfs v1.0.
|
||||
FlagEMENames
|
||||
// FlagGCMIV128 indicates 128-bit GCM IVs.
|
||||
// This flag is mandatory since gocryptfs v1.0.
|
||||
// This flag is mandatory since gocryptfs v1.0,
|
||||
// except when XChaCha20Poly1305 is used.
|
||||
FlagGCMIV128
|
||||
// FlagLongNames allows file names longer than 176 bytes.
|
||||
FlagLongNames
|
||||
@ -28,36 +29,26 @@ const (
|
||||
// FlagFIDO2 means that "-fido2" was used when creating the filesystem.
|
||||
// The masterkey is protected using a FIDO2 token instead of a password.
|
||||
FlagFIDO2
|
||||
// FlagXChaCha20Poly1305 means we use XChaCha20-Poly1305 file content encryption
|
||||
FlagXChaCha20Poly1305
|
||||
)
|
||||
|
||||
// knownFlags stores the known feature flags and their string representation
|
||||
var knownFlags = map[flagIota]string{
|
||||
FlagPlaintextNames: "PlaintextNames",
|
||||
FlagDirIV: "DirIV",
|
||||
FlagEMENames: "EMENames",
|
||||
FlagGCMIV128: "GCMIV128",
|
||||
FlagLongNames: "LongNames",
|
||||
FlagAESSIV: "AESSIV",
|
||||
FlagRaw64: "Raw64",
|
||||
FlagHKDF: "HKDF",
|
||||
FlagFIDO2: "FIDO2",
|
||||
}
|
||||
|
||||
// Filesystems that do not have these feature flags set are deprecated.
|
||||
var requiredFlagsNormal = []flagIota{
|
||||
FlagDirIV,
|
||||
FlagEMENames,
|
||||
FlagGCMIV128,
|
||||
}
|
||||
|
||||
// Filesystems without filename encryption obviously don't have or need the
|
||||
// filename related feature flags.
|
||||
var requiredFlagsPlaintextNames = []flagIota{
|
||||
FlagGCMIV128,
|
||||
FlagPlaintextNames: "PlaintextNames",
|
||||
FlagDirIV: "DirIV",
|
||||
FlagEMENames: "EMENames",
|
||||
FlagGCMIV128: "GCMIV128",
|
||||
FlagLongNames: "LongNames",
|
||||
FlagAESSIV: "AESSIV",
|
||||
FlagRaw64: "Raw64",
|
||||
FlagHKDF: "HKDF",
|
||||
FlagFIDO2: "FIDO2",
|
||||
FlagXChaCha20Poly1305: "XChaCha20Poly1305",
|
||||
}
|
||||
|
||||
// isFeatureFlagKnown verifies that we understand a feature flag.
|
||||
func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
|
||||
func isFeatureFlagKnown(flag string) bool {
|
||||
for _, knownFlag := range knownFlags {
|
||||
if knownFlag == flag {
|
||||
return true
|
||||
|
@ -1,6 +1,7 @@
|
||||
package configfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
@ -61,8 +62,9 @@ func NewScryptKDF(logN int) ScryptKDF {
|
||||
|
||||
// DeriveKey returns a new key from a supplied password.
|
||||
func (s *ScryptKDF) DeriveKey(pw []byte) []byte {
|
||||
s.validateParams()
|
||||
|
||||
if err := s.validateParams(); err != nil {
|
||||
os.Exit(exitcodes.ScryptParams)
|
||||
}
|
||||
k, err := scrypt.Key(pw, s.Salt, s.N, s.R, s.P, s.KeyLen)
|
||||
if err != nil {
|
||||
log.Panicf("DeriveKey failed: %v", err)
|
||||
@ -80,21 +82,22 @@ func (s *ScryptKDF) LogN() int {
|
||||
// If not, it exists with an error message.
|
||||
// This makes sure we do not get weak parameters passed through a
|
||||
// rougue gocryptfs.conf.
|
||||
func (s *ScryptKDF) validateParams() {
|
||||
func (s *ScryptKDF) validateParams() error {
|
||||
minN := 1 << scryptMinLogN
|
||||
if s.N < minN {
|
||||
os.Exit(exitcodes.ScryptParams)
|
||||
return fmt.Errorf("Fatal: scryptn below 10 is too low to make sense")
|
||||
}
|
||||
if s.R < scryptMinR {
|
||||
os.Exit(exitcodes.ScryptParams)
|
||||
return fmt.Errorf("Fatal: scrypt parameter R below minimum: value=%d, min=%d", s.R, scryptMinR)
|
||||
}
|
||||
if s.P < scryptMinP {
|
||||
os.Exit(exitcodes.ScryptParams)
|
||||
return fmt.Errorf("Fatal: scrypt parameter P below minimum: value=%d, min=%d", s.P, scryptMinP)
|
||||
}
|
||||
if len(s.Salt) < scryptMinSaltLen {
|
||||
os.Exit(exitcodes.ScryptParams)
|
||||
return fmt.Errorf("Fatal: scrypt salt length below minimum: value=%d, min=%d", len(s.Salt), scryptMinSaltLen)
|
||||
}
|
||||
if s.KeyLen < cryptocore.KeyLen {
|
||||
os.Exit(exitcodes.ScryptParams)
|
||||
return fmt.Errorf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", s.KeyLen, cryptocore.KeyLen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
67
internal/configfile/validate.go
Normal file
67
internal/configfile/validate.go
Normal file
@ -0,0 +1,67 @@
|
||||
package configfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"../contentenc"
|
||||
)
|
||||
|
||||
// Validate that the combination of settings makes sense and is supported
|
||||
func (cf *ConfFile) Validate() error {
|
||||
if cf.Version != contentenc.CurrentVersion {
|
||||
return fmt.Errorf("Unsupported on-disk format %d", cf.Version)
|
||||
}
|
||||
// scrypt params ok?
|
||||
if err := cf.ScryptObject.validateParams(); err != nil {
|
||||
return err
|
||||
}
|
||||
// All feature flags that are in the config file are known?
|
||||
for _, flag := range cf.FeatureFlags {
|
||||
if !isFeatureFlagKnown(flag) {
|
||||
return fmt.Errorf("Unknown feature flag %q", flag)
|
||||
}
|
||||
}
|
||||
// File content encryption
|
||||
{
|
||||
switch {
|
||||
case cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) && cf.IsFeatureFlagSet(FlagAESSIV):
|
||||
return fmt.Errorf("Can't have both XChaCha20Poly1305 and AESSIV feature flags")
|
||||
case cf.IsFeatureFlagSet(FlagAESSIV):
|
||||
if !cf.IsFeatureFlagSet(FlagGCMIV128) {
|
||||
return fmt.Errorf("AESSIV requires GCMIV128 feature flag")
|
||||
}
|
||||
case cf.IsFeatureFlagSet(FlagXChaCha20Poly1305):
|
||||
if cf.IsFeatureFlagSet(FlagGCMIV128) {
|
||||
return fmt.Errorf("XChaCha20Poly1305 conflicts with GCMIV128 feature flag")
|
||||
}
|
||||
if !cf.IsFeatureFlagSet(FlagHKDF) {
|
||||
return fmt.Errorf("XChaCha20Poly1305 requires HKDF feature flag")
|
||||
}
|
||||
// The absence of other flags means AES-GCM (oldest algorithm)
|
||||
case !cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) && !cf.IsFeatureFlagSet(FlagAESSIV):
|
||||
if !cf.IsFeatureFlagSet(FlagGCMIV128) {
|
||||
return fmt.Errorf("AES-GCM requires GCMIV128 feature flag")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Filename encryption
|
||||
{
|
||||
switch {
|
||||
case cf.IsFeatureFlagSet(FlagPlaintextNames) && cf.IsFeatureFlagSet(FlagEMENames):
|
||||
return fmt.Errorf("Can't have both PlaintextNames and EMENames feature flags")
|
||||
case cf.IsFeatureFlagSet(FlagPlaintextNames):
|
||||
if cf.IsFeatureFlagSet(FlagDirIV) {
|
||||
return fmt.Errorf("PlaintextNames conflicts with DirIV feature flag")
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagLongNames) {
|
||||
return fmt.Errorf("PlaintextNames conflicts with LongNames feature flag")
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagRaw64) {
|
||||
return fmt.Errorf("PlaintextNames conflicts with Raw64 feature flag")
|
||||
}
|
||||
case cf.IsFeatureFlagSet(FlagEMENames):
|
||||
// All combinations of DirIV, LongNames, Raw64 allowed
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -13,9 +13,6 @@ import (
|
||||
"../stupidgcm"
|
||||
)
|
||||
|
||||
// NonceMode determines how nonces are created.
|
||||
type NonceMode int
|
||||
|
||||
const (
|
||||
//value from FUSE doc
|
||||
MAX_KERNEL_WRITE = 128 * 1024
|
||||
@ -27,15 +24,6 @@ const (
|
||||
// master key in the config file is encrypted with a 96-bit IV for
|
||||
// gocryptfs v1.2 and earlier. v1.3 switched to 128 bit.
|
||||
DefaultIVBits = 128
|
||||
|
||||
_ = iota // skip zero
|
||||
// RandomNonce chooses a random nonce.
|
||||
RandomNonce NonceMode = iota
|
||||
// ReverseDeterministicNonce chooses a deterministic nonce, suitable for
|
||||
// use in reverse mode.
|
||||
ReverseDeterministicNonce NonceMode = iota
|
||||
// ExternalNonce derives a nonce from external sources.
|
||||
ExternalNonce NonceMode = iota
|
||||
)
|
||||
|
||||
// ContentEnc is used to encipher and decipher file content.
|
||||
@ -171,7 +159,7 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b
|
||||
nonce := ciphertext[:be.cryptoCore.IVLen]
|
||||
if bytes.Equal(nonce, be.allZeroNonce) {
|
||||
// Bug in tmpfs?
|
||||
// https://github.com/rfjakob/gocryptfs/issues/56
|
||||
// https://github.com/rfjakob/gocryptfs/v2/issues/56
|
||||
// http://www.spinics.net/lists/kernel/msg2370127.html
|
||||
return nil, errors.New("all-zero nonce")
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
|
||||
"github.com/rfjakob/eme"
|
||||
|
||||
"../siv_aead"
|
||||
@ -17,37 +18,35 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// KeyLen is the cipher key length in bytes. 32 for AES-256.
|
||||
// KeyLen is the cipher key length in bytes. All backends use 32 bytes.
|
||||
KeyLen = 32
|
||||
// AuthTagLen is the length of a GCM auth tag in bytes.
|
||||
// AuthTagLen is the length of a authentication tag in bytes.
|
||||
// All backends use 16 bytes.
|
||||
AuthTagLen = 16
|
||||
)
|
||||
|
||||
// AEADTypeEnum indicates the type of AEAD backend in use.
|
||||
type AEADTypeEnum int
|
||||
|
||||
const (
|
||||
// BackendOpenSSL specifies the OpenSSL backend.
|
||||
BackendOpenSSL AEADTypeEnum = 3
|
||||
// BackendGoGCM specifies the Go based GCM backend.
|
||||
BackendGoGCM AEADTypeEnum = 4
|
||||
// BackendAESSIV specifies an AESSIV backend.
|
||||
BackendAESSIV AEADTypeEnum = 5
|
||||
)
|
||||
|
||||
func (a AEADTypeEnum) String() string {
|
||||
switch a {
|
||||
case BackendOpenSSL:
|
||||
return "BackendOpenSSL"
|
||||
case BackendGoGCM:
|
||||
return "BackendGoGCM"
|
||||
case BackendAESSIV:
|
||||
return "BackendAESSIV"
|
||||
default:
|
||||
return fmt.Sprintf("%d", a)
|
||||
}
|
||||
type AEADTypeEnum struct {
|
||||
Name string
|
||||
NonceSize int
|
||||
}
|
||||
|
||||
// BackendOpenSSL specifies the OpenSSL backend.
|
||||
// "AES-GCM-256-OpenSSL" in gocryptfs -speed.
|
||||
var BackendOpenSSL AEADTypeEnum = AEADTypeEnum{"AES-GCM-256-OpenSSL", 16}
|
||||
|
||||
// BackendGoGCM specifies the Go based GCM backend.
|
||||
// "AES-GCM-256-Go" in gocryptfs -speed.
|
||||
var BackendGoGCM AEADTypeEnum = AEADTypeEnum{"AES-GCM-256-Go", 16}
|
||||
|
||||
// BackendAESSIV specifies an AESSIV backend.
|
||||
// "AES-SIV-512-Go" in gocryptfs -speed.
|
||||
var BackendAESSIV AEADTypeEnum = AEADTypeEnum{"AES-SIV-512-Go", siv_aead.NonceSize}
|
||||
|
||||
// BackendXChaCha20Poly1305 specifies XChaCha20-Poly1305-Go.
|
||||
// "XChaCha20-Poly1305-Go" in gocryptfs -speed.
|
||||
var BackendXChaCha20Poly1305 AEADTypeEnum = AEADTypeEnum{"XChaCha20-Poly1305-Go", chacha20poly1305.NonceSizeX}
|
||||
|
||||
// CryptoCore is the low level crypto implementation.
|
||||
type CryptoCore struct {
|
||||
// EME is used for filename encryption.
|
||||
@ -58,7 +57,8 @@ type CryptoCore struct {
|
||||
AEADBackend AEADTypeEnum
|
||||
// GCM needs unique IVs (nonces)
|
||||
IVGenerator *nonceGenerator
|
||||
IVLen int
|
||||
// IVLen in bytes
|
||||
IVLen int
|
||||
}
|
||||
|
||||
// New returns a new CryptoCore object or panics.
|
||||
@ -71,10 +71,11 @@ type CryptoCore struct {
|
||||
// a config file) or the masterkey (when finally mounting the filesystem).
|
||||
func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDecode bool) *CryptoCore {
|
||||
if len(key) != KeyLen {
|
||||
log.Panic(fmt.Sprintf("Unsupported key length %d", len(key)))
|
||||
log.Panicf("Unsupported key length of %d bytes", len(key))
|
||||
}
|
||||
if IVBitLen != 96 && IVBitLen != 128 && IVBitLen != chacha20poly1305.NonceSizeX*8 {
|
||||
log.Panicf("Unsupported IV length of %d bits", IVBitLen)
|
||||
}
|
||||
// We want the IV size in bytes
|
||||
IVLen := IVBitLen / 8
|
||||
|
||||
// Initialize EME for filename encryption.
|
||||
var emeCipher *eme.EMECipher
|
||||
@ -103,12 +104,14 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
|
||||
if useHKDF {
|
||||
gcmKey = hkdfDerive(key, hkdfInfoGCMContent, KeyLen)
|
||||
} else {
|
||||
// Filesystems created by gocryptfs v0.7 through v1.2 don't use HKDF.
|
||||
// Example: tests/example_filesystems/v0.9
|
||||
gcmKey = append([]byte{}, key...)
|
||||
}
|
||||
switch aeadType {
|
||||
case BackendOpenSSL:
|
||||
if IVLen != 16 {
|
||||
log.Panic("stupidgcm only supports 128-bit IVs")
|
||||
if IVBitLen != 128 {
|
||||
log.Panicf("stupidgcm only supports 128-bit IVs, you wanted %d", IVBitLen)
|
||||
}
|
||||
aeadCipher = stupidgcm.New(gcmKey, forceDecode)
|
||||
case BackendGoGCM:
|
||||
@ -116,7 +119,7 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
aeadCipher, err = cipher.NewGCMWithNonceSize(goGcmBlockCipher, IVLen)
|
||||
aeadCipher, err = cipher.NewGCMWithNonceSize(goGcmBlockCipher, IVBitLen/8)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
@ -125,9 +128,9 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
|
||||
gcmKey[i] = 0
|
||||
}
|
||||
} else if aeadType == BackendAESSIV {
|
||||
if IVLen != 16 {
|
||||
// SIV supports any nonce size, but we only use 16.
|
||||
log.Panic("AES-SIV must use 16-byte nonces")
|
||||
if IVBitLen != 128 {
|
||||
// SIV supports any nonce size, but we only use 128.
|
||||
log.Panicf("AES-SIV must use 128-bit IVs, you wanted %d", IVBitLen)
|
||||
}
|
||||
// AES-SIV uses 1/2 of the key for authentication, 1/2 for
|
||||
// encryption, so we need a 64-bytes key for AES-256. Derive it from
|
||||
@ -144,16 +147,34 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
|
||||
for i := range key64 {
|
||||
key64[i] = 0
|
||||
}
|
||||
} else if aeadType == BackendXChaCha20Poly1305 {
|
||||
// We don't support legacy modes with XChaCha20-Poly1305
|
||||
if IVBitLen != chacha20poly1305.NonceSizeX*8 {
|
||||
log.Panicf("XChaCha20-Poly1305 must use 192-bit IVs, you wanted %d", IVBitLen)
|
||||
}
|
||||
if !useHKDF {
|
||||
log.Panic("XChaCha20-Poly1305 must use HKDF, but it is disabled")
|
||||
}
|
||||
derivedKey := hkdfDerive(key, hkdfInfoXChaChaPoly1305Content, chacha20poly1305.KeySize)
|
||||
aeadCipher, err = chacha20poly1305.NewX(derivedKey)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
} else {
|
||||
log.Panic("unknown backend cipher")
|
||||
log.Panicf("unknown cipher backend %q", aeadType.Name)
|
||||
}
|
||||
|
||||
if aeadCipher.NonceSize()*8 != IVBitLen {
|
||||
log.Panicf("Mismatched aeadCipher.NonceSize*8=%d and IVBitLen=%d bits",
|
||||
aeadCipher.NonceSize()*8, IVBitLen)
|
||||
}
|
||||
|
||||
return &CryptoCore{
|
||||
EMECipher: emeCipher,
|
||||
AEADCipher: aeadCipher,
|
||||
AEADBackend: aeadType,
|
||||
IVGenerator: &nonceGenerator{nonceLen: IVLen},
|
||||
IVLen: IVLen,
|
||||
IVGenerator: &nonceGenerator{nonceLen: IVBitLen / 8},
|
||||
IVLen: IVBitLen / 8,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,10 @@ import (
|
||||
const (
|
||||
// "info" data that HKDF mixes into the generated key to make it unique.
|
||||
// For convenience, we use a readable string.
|
||||
hkdfInfoEMENames = "EME filename encryption"
|
||||
hkdfInfoGCMContent = "AES-GCM file content encryption"
|
||||
hkdfInfoSIVContent = "AES-SIV file content encryption"
|
||||
hkdfInfoEMENames = "EME filename encryption"
|
||||
hkdfInfoGCMContent = "AES-GCM file content encryption"
|
||||
hkdfInfoSIVContent = "AES-SIV file content encryption"
|
||||
hkdfInfoXChaChaPoly1305Content = "XChaCha20-Poly1305 file content encryption"
|
||||
)
|
||||
|
||||
// hkdfDerive derives "outLen" bytes from "masterkey" and "info" using
|
||||
|
@ -22,7 +22,11 @@ const (
|
||||
// ReadDirIVAt reads "gocryptfs.diriv" from the directory that is opened as "dirfd".
|
||||
// Using the dirfd makes it immune to concurrent renames of the directory.
|
||||
// Retries on EINTR.
|
||||
func ReadDirIVAt(dirfd int) (iv []byte, err error) {
|
||||
// If deterministicNames is set it returns an all-zero slice.
|
||||
func (n *NameTransform) ReadDirIVAt(dirfd int) (iv []byte, err error) {
|
||||
if n.deterministicNames {
|
||||
return make([]byte, DirIVLen), nil
|
||||
}
|
||||
fdRaw, err := syscallcompat.Openat(dirfd, DirIVFilename,
|
||||
syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
|
||||
if err != nil {
|
||||
@ -63,7 +67,7 @@ func WriteDirIVAt(dirfd int) error {
|
||||
iv := cryptocore.RandBytes(DirIVLen)
|
||||
// 0400 permissions: gocryptfs.diriv should never be modified after creation.
|
||||
// Don't use "ioutil.WriteFile", it causes trouble on NFS:
|
||||
// https://github.com/rfjakob/gocryptfs/commit/7d38f80a78644c8ec4900cc990bfb894387112ed
|
||||
// https://github.com/rfjakob/gocryptfs/v2/commit/7d38f80a78644c8ec4900cc990bfb894387112ed
|
||||
fd, err := syscallcompat.Openat(dirfd, DirIVFilename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, dirivPerms)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -124,7 +124,7 @@ func (n *NameTransform) WriteLongNameAt(dirfd int, hashName string, plainName st
|
||||
plainName = filepath.Base(plainName)
|
||||
|
||||
// Encrypt the basename
|
||||
dirIV, err := ReadDirIVAt(dirfd)
|
||||
dirIV, err := n.ReadDirIVAt(dirfd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -23,20 +23,22 @@ type NameTransform struct {
|
||||
// on the Raw64 feature flag
|
||||
B64 *base64.Encoding
|
||||
// Patterns to bypass decryption
|
||||
badnamePatterns []string
|
||||
badnamePatterns []string
|
||||
deterministicNames bool
|
||||
}
|
||||
|
||||
// New returns a new NameTransform instance.
|
||||
func New(e *eme.EMECipher, longNames bool, raw64 bool, badname []string) *NameTransform {
|
||||
func New(e *eme.EMECipher, longNames bool, raw64 bool, badname []string, deterministicNames bool) *NameTransform {
|
||||
b64 := base64.URLEncoding
|
||||
if raw64 {
|
||||
b64 = base64.RawURLEncoding
|
||||
}
|
||||
return &NameTransform{
|
||||
emeCipher: e,
|
||||
longNames: longNames,
|
||||
B64: b64,
|
||||
badnamePatterns: badname,
|
||||
emeCipher: e,
|
||||
longNames: longNames,
|
||||
B64: b64,
|
||||
badnamePatterns: badname,
|
||||
deterministicNames: deterministicNames,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,14 +6,14 @@ const (
|
||||
// never chmod'ed or chown'ed.
|
||||
//
|
||||
// Group-readable so the FS can be mounted by several users in the same group
|
||||
// (see https://github.com/rfjakob/gocryptfs/issues/387 ).
|
||||
// (see https://github.com/rfjakob/gocryptfs/v2/issues/387 ).
|
||||
//
|
||||
// Note that gocryptfs.conf is still created with 0400 permissions so the
|
||||
// owner must explicitly chmod it to permit access.
|
||||
//
|
||||
// World-readable so an encrypted directory can be copied by the non-root
|
||||
// owner when gocryptfs is running as root
|
||||
// ( https://github.com/rfjakob/gocryptfs/issues/539 ).
|
||||
// ( https://github.com/rfjakob/gocryptfs/v2/issues/539 ).
|
||||
dirivPerms = 0444
|
||||
|
||||
// Permissions for gocryptfs.longname.[sha256].name files.
|
||||
|
@ -19,6 +19,11 @@ const (
|
||||
// KeyLen is the required key length. The SIV algorithm supports other lengths,
|
||||
// but we only support 64.
|
||||
KeyLen = 64
|
||||
// NonceSize is the required nonce/IV length.
|
||||
// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
|
||||
NonceSize = 16
|
||||
// Overhead is the number of bytes added for integrity checking
|
||||
Overhead = 16
|
||||
)
|
||||
|
||||
// New returns a new cipher.AEAD implementation.
|
||||
@ -42,11 +47,11 @@ func new2(keyIn []byte) cipher.AEAD {
|
||||
|
||||
func (s *sivAead) NonceSize() int {
|
||||
// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
|
||||
return 16
|
||||
return NonceSize
|
||||
}
|
||||
|
||||
func (s *sivAead) Overhead() int {
|
||||
return 16
|
||||
return Overhead
|
||||
}
|
||||
|
||||
// Seal encrypts "in" using "nonce" and "authData" and appends the result to "dst"
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
// 2) Is ARM64 && has AES instructions && Go is v1.11 or higher
|
||||
// (commit https://github.com/golang/go/commit/4f1f503373cda7160392be94e3849b0c9b9ebbda)
|
||||
//
|
||||
// See https://github.com/rfjakob/gocryptfs/wiki/CPU-Benchmarks
|
||||
// See https://github.com/rfjakob/gocryptfs/v2/wiki/CPU-Benchmarks
|
||||
// for benchmarks.
|
||||
func PreferOpenSSL() bool {
|
||||
if BuiltWithoutOpenssl {
|
||||
@ -26,7 +26,7 @@ func PreferOpenSSL() bool {
|
||||
return false
|
||||
}
|
||||
// On the Apple M1, Go stdlib is faster than OpenSSL, despite cpu.ARM64.HasAES
|
||||
// reading false: https://github.com/rfjakob/gocryptfs/issues/556#issuecomment-848079309
|
||||
// reading false: https://github.com/rfjakob/gocryptfs/v2/issues/556#issuecomment-848079309
|
||||
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
|
||||
return false
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ func (g *StupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
|
||||
if res != 1 {
|
||||
// The error code must always be checked by the calling function, because the decrypted buffer
|
||||
// may contain corrupted data that we are returning in case the user forced reads
|
||||
if g.forceDecode == true {
|
||||
if g.forceDecode {
|
||||
return append(dst, buf...), ErrAuth
|
||||
}
|
||||
return nil, ErrAuth
|
||||
|
@ -1,52 +0,0 @@
|
||||
// +build without_openssl
|
||||
|
||||
package stupidgcm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"../exitcodes"
|
||||
)
|
||||
|
||||
type StupidGCM struct{}
|
||||
|
||||
const (
|
||||
// BuiltWithoutOpenssl indicates if openssl been disabled at compile-time
|
||||
BuiltWithoutOpenssl = true
|
||||
)
|
||||
|
||||
func errExit() {
|
||||
fmt.Fprintln(os.Stderr, "gocryptfs has been compiled without openssl support but you are still trying to use openssl")
|
||||
os.Exit(exitcodes.OpenSSL)
|
||||
}
|
||||
|
||||
func New(_ []byte, _ bool) *StupidGCM {
|
||||
errExit()
|
||||
// Never reached
|
||||
return &StupidGCM{}
|
||||
}
|
||||
|
||||
func (g *StupidGCM) NonceSize() int {
|
||||
errExit()
|
||||
return -1
|
||||
}
|
||||
|
||||
func (g *StupidGCM) Overhead() int {
|
||||
errExit()
|
||||
return -1
|
||||
}
|
||||
|
||||
func (g *StupidGCM) Seal(_, _, _, _ []byte) []byte {
|
||||
errExit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *StupidGCM) Open(_, _, _, _ []byte) ([]byte, error) {
|
||||
errExit()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *StupidGCM) Wipe() {
|
||||
errExit()
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
// https://github.com/golang/go/blob/d2a80f3fb5b44450e0b304ac5a718f99c053d82a/src/os/file_posix.go#L243
|
||||
//
|
||||
// This is needed because CIFS throws lots of EINTR errors:
|
||||
// https://github.com/rfjakob/gocryptfs/issues/483
|
||||
// https://github.com/rfjakob/gocryptfs/v2/issues/483
|
||||
//
|
||||
// Don't use retryEINTR() with syscall.Close()!
|
||||
// See https://code.google.com/p/chromium/issues/detail?id=269623 .
|
||||
|
@ -19,7 +19,7 @@ const sizeofDirent = int(unsafe.Sizeof(unix.Dirent{}))
|
||||
|
||||
// maxReclen sanity check: Reclen should never be larger than this.
|
||||
// Due to padding between entries, it is 280 even on 32-bit architectures.
|
||||
// See https://github.com/rfjakob/gocryptfs/issues/197 for details.
|
||||
// See https://github.com/rfjakob/gocryptfs/v2/issues/197 for details.
|
||||
const maxReclen = 280
|
||||
|
||||
type DirEntry struct {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package syscallcompat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@ -27,21 +26,6 @@ func Readlinkat(dirfd int, path string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Faccessat exists both in Linux and in MacOS 10.10+, but the Linux version
|
||||
// DOES NOT support any flags. Emulate AT_SYMLINK_NOFOLLOW like glibc does.
|
||||
func Faccessat(dirfd int, path string, mode uint32) error {
|
||||
var st unix.Stat_t
|
||||
err := Fstatat(dirfd, path, &st, unix.AT_SYMLINK_NOFOLLOW)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if st.Mode&syscall.S_IFMT == syscall.S_IFLNK {
|
||||
// Pretend that a symlink is always accessible
|
||||
return nil
|
||||
}
|
||||
return unix.Faccessat(dirfd, path, mode, 0)
|
||||
}
|
||||
|
||||
// Openat wraps the Openat syscall.
|
||||
// Retries on EINTR.
|
||||
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
|
||||
@ -57,15 +41,6 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
|
||||
return fd, err
|
||||
}
|
||||
|
||||
// Fchownat syscall.
|
||||
func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
|
||||
// Why would we ever want to call this without AT_SYMLINK_NOFOLLOW?
|
||||
if flags&unix.AT_SYMLINK_NOFOLLOW == 0 {
|
||||
flags |= unix.AT_SYMLINK_NOFOLLOW
|
||||
}
|
||||
return unix.Fchownat(dirfd, path, uid, gid, flags)
|
||||
}
|
||||
|
||||
// Fstatat syscall.
|
||||
// Retries on EINTR.
|
||||
func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) {
|
||||
@ -100,118 +75,3 @@ const XATTR_BUFSZ = XATTR_SIZE_MAX + 1024
|
||||
|
||||
// We try with a small buffer first - this one can be allocated on the stack.
|
||||
const XATTR_BUFSZ_SMALL = 500
|
||||
|
||||
// Fgetxattr is a wrapper around unix.Fgetxattr that handles the buffer sizing.
|
||||
func Fgetxattr(fd int, attr string) (val []byte, err error) {
|
||||
fn := func(buf []byte) (int, error) {
|
||||
return unix.Fgetxattr(fd, attr, buf)
|
||||
}
|
||||
return getxattrSmartBuf(fn)
|
||||
}
|
||||
|
||||
// Lgetxattr is a wrapper around unix.Lgetxattr that handles the buffer sizing.
|
||||
func Lgetxattr(path string, attr string) (val []byte, err error) {
|
||||
fn := func(buf []byte) (int, error) {
|
||||
return unix.Lgetxattr(path, attr, buf)
|
||||
}
|
||||
return getxattrSmartBuf(fn)
|
||||
}
|
||||
|
||||
func getxattrSmartBuf(fn func(buf []byte) (int, error)) ([]byte, error) {
|
||||
// Fastpaths. Important for security.capabilities, which gets queried a lot.
|
||||
buf := make([]byte, XATTR_BUFSZ_SMALL)
|
||||
sz, err := fn(buf)
|
||||
// Non-existing xattr
|
||||
if err == unix.ENODATA {
|
||||
return nil, err
|
||||
}
|
||||
// Underlying fs does not support security.capabilities (example: tmpfs)
|
||||
if err == unix.EOPNOTSUPP {
|
||||
return nil, err
|
||||
}
|
||||
// Small xattr
|
||||
if err == nil && sz < len(buf) {
|
||||
goto out
|
||||
}
|
||||
// Generic slowpath
|
||||
//
|
||||
// If the buffer is too small to fit the value, Linux and MacOS react
|
||||
// differently:
|
||||
// Linux: returns an ERANGE error and "-1" bytes.
|
||||
// MacOS: truncates the value and returns "size" bytes.
|
||||
//
|
||||
// We choose the simple approach of buffer that is bigger than the limit on
|
||||
// Linux, and return an error for everything that is bigger (which can
|
||||
// only happen on MacOS).
|
||||
buf = make([]byte, XATTR_BUFSZ)
|
||||
sz, err = fn(buf)
|
||||
if err == syscall.ERANGE {
|
||||
// Do NOT return ERANGE - the user might retry ad inifinitum!
|
||||
return nil, syscall.EOVERFLOW
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sz >= XATTR_SIZE_MAX {
|
||||
return nil, syscall.EOVERFLOW
|
||||
}
|
||||
out:
|
||||
// Copy only the actually used bytes to a new (smaller) buffer
|
||||
// so "buf" never leaves the function and can be allocated on the stack.
|
||||
val := make([]byte, sz)
|
||||
copy(val, buf)
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// Flistxattr is a wrapper for unix.Flistxattr that handles buffer sizing and
|
||||
// parsing the returned blob to a string slice.
|
||||
func Flistxattr(fd int) (attrs []string, err error) {
|
||||
// See the buffer sizing comments in getxattrSmartBuf.
|
||||
// TODO: smarter buffer sizing?
|
||||
buf := make([]byte, XATTR_BUFSZ)
|
||||
sz, err := unix.Flistxattr(fd, buf)
|
||||
if err == syscall.ERANGE {
|
||||
// Do NOT return ERANGE - the user might retry ad inifinitum!
|
||||
return nil, syscall.EOVERFLOW
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sz >= XATTR_SIZE_MAX {
|
||||
return nil, syscall.EOVERFLOW
|
||||
}
|
||||
attrs = parseListxattrBlob(buf[:sz])
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
// Llistxattr is a wrapper for unix.Llistxattr that handles buffer sizing and
|
||||
// parsing the returned blob to a string slice.
|
||||
func Llistxattr(path string) (attrs []string, err error) {
|
||||
// TODO: smarter buffer sizing?
|
||||
buf := make([]byte, XATTR_BUFSZ)
|
||||
sz, err := unix.Llistxattr(path, buf)
|
||||
if err == syscall.ERANGE {
|
||||
// Do NOT return ERANGE - the user might retry ad inifinitum!
|
||||
return nil, syscall.EOVERFLOW
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sz >= XATTR_SIZE_MAX {
|
||||
return nil, syscall.EOVERFLOW
|
||||
}
|
||||
attrs = parseListxattrBlob(buf[:sz])
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func parseListxattrBlob(buf []byte) (attrs []string) {
|
||||
parts := bytes.Split(buf, []byte{0})
|
||||
for _, part := range parts {
|
||||
if len(part) == 0 {
|
||||
// Last part is empty, ignore
|
||||
continue
|
||||
}
|
||||
attrs = append(attrs, string(part))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ func EnospcPrealloc(fd int, off int64, len int64) (err error) {
|
||||
}
|
||||
if err == syscall.EOPNOTSUPP {
|
||||
// ZFS and ext3 do not support fallocate. Warn but continue anyway.
|
||||
// https://github.com/rfjakob/gocryptfs/issues/22
|
||||
// https://github.com/rfjakob/gocryptfs/v2/issues/22
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
13
volume.go
13
volume.go
@ -75,7 +75,7 @@ func registerNewVolume(rootCipherDir string, masterkey []byte, cf *configfile.Co
|
||||
newVolume.cryptoCore = cryptocore.New(masterkey, cryptoBackend, contentenc.DefaultIVBits, true, forcedecode)
|
||||
newVolume.contentEnc = contentenc.New(newVolume.cryptoCore, contentenc.DefaultBS, forcedecode)
|
||||
var badname []string
|
||||
newVolume.nameTransform = nametransform.New(newVolume.cryptoCore.EMECipher, true, true, badname)
|
||||
newVolume.nameTransform = nametransform.New(newVolume.cryptoCore.EMECipher, true, true, badname, false)
|
||||
|
||||
//copying rootCipherDir
|
||||
var grcd strings.Builder
|
||||
@ -156,7 +156,16 @@ func gcf_change_password(rootCipherDir string, oldPassword, givenScryptHash, new
|
||||
|
||||
//export gcf_create_volume
|
||||
func gcf_create_volume(rootCipherDir string, password []byte, plaintextNames bool, logN int, creator string) bool {
|
||||
err := configfile.Create(filepath.Join(rootCipherDir, configfile.ConfDefaultName), password, plaintextNames, logN, creator, false, false, nil, nil)
|
||||
err := configfile.Create(&configfile.CreateArgs{
|
||||
Filename: filepath.Join(rootCipherDir, configfile.ConfDefaultName),
|
||||
Password: password,
|
||||
PlaintextNames: plaintextNames,
|
||||
LogN: logN,
|
||||
Creator: creator,
|
||||
AESSIV: false,
|
||||
DeterministicNames: false,
|
||||
XChaCha20Poly1305: false,
|
||||
})
|
||||
if err == nil {
|
||||
if plaintextNames {
|
||||
return true
|
||||
|
Loading…
Reference in New Issue
Block a user