Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for comments: Allow more than one encrypted master key #760

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}/..",
// "args": ["-init", "-fido2", "/dev/hidraw3", "-fido2-name", "solo", "cipher2"],
"args": ["-user", "testuser", "-add-fido2-device", "/dev/hidraw3", "-add-fido2", "solo", "/mnt/home/tpasch/tmp/cipher2"],
// "cwd": "/mnt/home/tpasch/tmp"
"console": "integratedTerminal"
}
]
}
50 changes: 49 additions & 1 deletion cli_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type argContainer struct {
dev, nodev, suid, nosuid, exec, noexec, rw, ro, kernel_cache, acl bool
masterkey, mountpoint, cipherdir, cpuprofile,
memprofile, ko, ctlsock, fsname, force_owner, trace, fido2 string
// more than one encryption of masterkey
user, fido2Name, addUser, deleteUser, addFido2, addFido2Name, deleteFido2Name string
// -extpass, -badname, -passfile can be passed multiple times
extpass, badname, passfile []string
// For reverse mode, several ways to specify exclusions. All can be specified multiple times.
Expand Down Expand Up @@ -207,7 +209,17 @@ func parseCliOpts(osArgs []string) (args argContainer) {
flagSet.StringVar(&args.fsname, "fsname", "", "Override the filesystem name")
flagSet.StringVar(&args.force_owner, "force_owner", "", "uid:gid pair to coerce ownership")
flagSet.StringVar(&args.trace, "trace", "", "Write execution trace to file")
flagSet.StringVar(&args.fido2, "fido2", "", "Protect the masterkey using a FIDO2 token instead of a password")
flagSet.StringVar(&args.fido2, "fido2", "", "Protect the masterkey using the FIDO2 device at <fido2> (no password needed in this case)")

// more than one encryption of masterkey
flagSet.StringVar(&args.user, "user", configfile.DefaultKey, "Use <user> instead of "+configfile.DefaultKey+" for decryption of masterkey")
flagSet.StringVar(&args.fido2Name, "fido2-name", configfile.DefaultKey, "Use <fido2Name> instead of "+configfile.DefaultKey+" for fido2 device registration or decryption")
flagSet.StringVar(&args.addUser, "add-user", "", "Add encrypted masterkey for <addUser> using credentials of <user>")
flagSet.StringVar(&args.deleteUser, "delete-user", "", "Delete encrypted masterkey for <deleteUser> using credentials of <user>")
flagSet.StringVar(&args.addFido2, "add-fido2", "", "Add FIDO2 device on path <addFido2> for masterkey decryption")
flagSet.StringVar(&args.addFido2Name, "add-fido2-name", configfile.DefaultKey,
"Add FIDO2 device with name <addFido2Name> instead of "+configfile.DefaultKey+" for masterkey decryption")
flagSet.StringVar(&args.deleteFido2Name, "delete-fido2-name", "", "Delete encrypted masterkey of FIDO2 device with name <deleteFido2Name> instead of "+configfile.DefaultKey)

// Exclusion options
flagSet.StringArrayVar(&args.exclude, "e", nil, "Alias for -exclude")
Expand Down Expand Up @@ -288,6 +300,30 @@ func parseCliOpts(osArgs []string) (args argContainer) {
tlog.Fatal.Printf("The options -extpass and -fido2 cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if args.user != configfile.DefaultKey && args.fido2 != "" {
tlog.Fatal.Printf("The options -user and -fido2 cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if args.user != configfile.DefaultKey && args.fido2Name != configfile.DefaultKey {
tlog.Fatal.Printf("The options -user and -fido2-name cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if args.addUser != "" && args.addFido2 != "" {
tlog.Fatal.Printf("The options -add-user and -add-fido2 cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if args.addUser != "" && args.addFido2Name != configfile.DefaultKey {
tlog.Fatal.Printf("The options -add-user and -add-fido2-name cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if args.addUser != "" && args.deleteUser != "" {
tlog.Fatal.Printf("The options -add-user and -delete-user cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if args.deleteUser != "" && args.deleteFido2Name != "" {
tlog.Fatal.Printf("The options -delete-user and -delete-fido2-name cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if args.idle < 0 {
tlog.Fatal.Printf("Idle timeout cannot be less than 0")
os.Exit(exitcodes.Usage)
Expand Down Expand Up @@ -331,6 +367,18 @@ func countOpFlags(args *argContainer) int {
if args.fsck {
count++
}
if args.addUser != "" {
count++
}
if args.deleteUser != "" {
count++
}
if args.addFido2 != "" {
count++
}
if args.deleteFido2Name != "" {
count++
}
return count
}

Expand Down
16 changes: 10 additions & 6 deletions cli_args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"reflect"
"testing"

"github.com/rfjakob/gocryptfs/v2/internal/configfile"
"github.com/rfjakob/gocryptfs/v2/internal/stupidgcm"
)

Expand Down Expand Up @@ -116,12 +117,15 @@ func TestConvertToDoubleDash(t *testing.T) {

func TestParseCliOpts(t *testing.T) {
defaultArgs := argContainer{
longnames: true,
longnamemax: 255,
raw64: true,
hkdf: true,
openssl: stupidgcm.PreferOpenSSLAES256GCM(), // depends on CPU and build flags
scryptn: 16,
longnames: true,
longnamemax: 255,
raw64: true,
hkdf: true,
openssl: stupidgcm.PreferOpenSSLAES256GCM(), // depends on CPU and build flags
scryptn: 16,
user: configfile.DefaultKey,
fido2Name: configfile.DefaultKey,
addFido2Name: configfile.DefaultKey,
}

type testcaseContainer struct {
Expand Down
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ require (
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
)


// replace (
// "github.com/rfjakob/gocryptfs/v2/internal/configfile" => "./internal/configfile"
//)

21 changes: 14 additions & 7 deletions gocryptfs-xray/xray_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ type argContainer struct {
xchacha *bool
sep0 *bool
fido2 *string
fido2Name *string
version *bool
user *string
}

func main() {
Expand All @@ -95,7 +97,9 @@ func main() {
args.aessiv = flag.Bool("aessiv", false, "Assume AES-SIV mode instead of AES-GCM")
args.xchacha = flag.Bool("xchacha", false, "Assume XChaCha20-Poly1305 mode instead of AES-GCM")
args.fido2 = flag.String("fido2", "", "Protect the masterkey using a FIDO2 token instead of a password")
args.fido2Name = flag.String("fido2-name", configfile.DefaultKey, "Use <fido2Name> instead of "+configfile.DefaultKey+" for fido2 device registration or decryption")
args.version = flag.Bool("version", false, "Print version information")
args.user = flag.String("user", configfile.DefaultKey, "Use <user> instead of "+configfile.DefaultKey+" for decryption of masterkey")

flag.Usage = usage
flag.Parse()
Expand Down Expand Up @@ -127,34 +131,37 @@ func main() {
}
defer f.Close()
if *args.dumpmasterkey {
dumpMasterKey(fn, *args.fido2)
dumpMasterKey(fn, *args.user, *args.fido2, *args.fido2Name)
} else {
inspectCiphertext(&args, f)
}
}

func dumpMasterKey(fn string, fido2Path string) {
func dumpMasterKey(fn string, user string, fido2Path string, fido2Name string) {
tlog.Info.Enabled = false
cf, err := configfile.Load(fn)
if err != nil {
fmt.Fprintln(os.Stderr, err)
exitcodes.Exit(err)
}
var pw []byte
if cf.IsFeatureFlagSet(configfile.FlagFIDO2) {
if fido2Path == "" {
tlog.Fatal.Printf("Masterkey encrypted using FIDO2 token; need to use the --fido2 option.")
if cf.IsFeatureFlagSet(configfile.FlagFIDO2) && fido2Path != "" {
var fido2Obj = cf.FIDO2[fido2Name]
if fido2Obj == nil {
tlog.Fatal.Printf("Masterkey encrypted using FIDO2 token; password not found: check your --fido2-name option")
os.Exit(exitcodes.Usage)
}
pw = fido2.Secret(fido2Path, cf.FIDO2.CredentialID, cf.FIDO2.HMACSalt)
pw = fido2.Secret(fido2Path, fido2Obj.CredentialID, fido2Obj.HMACSalt)
// overwrite user to match fido2Name
user = fido2Name
} else {
pw, err = readpassword.Once(nil, nil, "")
if err != nil {
tlog.Fatal.Println(err)
os.Exit(exitcodes.ReadPassword)
}
}
masterkey, err := cf.DecryptMasterKey(pw)
masterkey, err := cf.DecryptMasterKey(user, pw)
// Purge password from memory
for i := range pw {
pw[i] = 0
Expand Down
6 changes: 4 additions & 2 deletions gocryptfs-xray/xray_tests/aesgcm_fs/gocryptfs.conf
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
{
"Creator": "gocryptfs v1.7-beta1-11-g8d71f8f-dirty",
"EncryptedKey": "Rp0VYTJ9QK2imhJQH1miFIgAYZbsfv3t1tvPJvsOVy86ogBzKpUuMDFXD+PPawLvZM/TuYl0n3gx1RY5hzFfbg==",
"EncryptedKeys": {
"DefaultKey": "Rp0VYTJ9QK2imhJQH1miFIgAYZbsfv3t1tvPJvsOVy86ogBzKpUuMDFXD+PPawLvZM/TuYl0n3gx1RY5hzFfbg=="
};
"ScryptObject": {
"Salt": "mDPjzd+SZpScPYVv/M9AFjNzXcUy6fqKckXay53EQdQ=",
"N": 1024,
"R": 8,
"P": 1,
"KeyLen": 32
},
"Version": 2,
"Version": 3,
"FeatureFlags": [
"GCMIV128",
"HKDF",
Expand Down
6 changes: 4 additions & 2 deletions gocryptfs-xray/xray_tests/aessiv_fs/gocryptfs.conf
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
{
"Creator": "gocryptfs v1.7-beta1-11-g8d71f8f-dirty",
"EncryptedKey": "YOxpZ+cImv4HirwuwIUpRmOMlyAFRvEqHOXdgpMcGvIlm70h4q+shSr3RZ19xomnbFZXGfIfKQ2APtVYWOAwuw==",
"EncryptedKeys": {
"DefaultKey": "YOxpZ+cImv4HirwuwIUpRmOMlyAFRvEqHOXdgpMcGvIlm70h4q+shSr3RZ19xomnbFZXGfIfKQ2APtVYWOAwuw=="
},
"ScryptObject": {
"Salt": "OzdcVESNmkD0403NHBWezQmq2SyDyLOY2/B4Aev2lHc=",
"N": 1024,
"R": 8,
"P": 1,
"KeyLen": 32
},
"Version": 2,
"Version": 3,
"FeatureFlags": [
"GCMIV128",
"HKDF",
Expand Down
19 changes: 19 additions & 0 deletions gocryptfs.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"gopls": {
"ui.semanticTokens": true
}
},
"extensions": {
"recommendations": [
"msyrus.go-doc",
"766b.go-outliner"
],
"unwantedRecommendations": []
}
}
2 changes: 1 addition & 1 deletion info.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func info(filename string) {
// Pretty-print
fmt.Printf("Creator: %s\n", cf.Creator)
fmt.Printf("FeatureFlags: %s\n", strings.Join(cf.FeatureFlags, " "))
fmt.Printf("EncryptedKey: %dB\n", len(cf.EncryptedKey))
fmt.Printf("EncryptedKey: %dB\n", len(cf.EncryptedKeys[configfile.DefaultKey]))
fmt.Printf("ScryptObject: Salt=%dB N=%d R=%d P=%d KeyLen=%d\n",
len(s.Salt), s.N, s.R, s.P, s.KeyLen)
fmt.Printf("contentEncryption: %s\n", algo.Algo) // lowercase because not in JSON
Expand Down
11 changes: 9 additions & 2 deletions init_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,33 +76,40 @@ func initDir(args *argContainer) {
}
// Choose password for config file
if len(args.extpass) == 0 && args.fido2 == "" {
tlog.Info.Printf("Choose a password for protecting your files.")
tlog.Info.Printf("As user %v, choose a password for protecting your files.", args.user)
}
{
var password []byte
var fido2Name string
var fido2CredentialID, fido2HmacSalt []byte
if args.fido2 != "" {
fido2CredentialID = fido2.Register(args.fido2, filepath.Base(args.cipherdir))
fido2Name = args.fido2Name
fido2CredentialID = fido2.Register(args.fido2, fido2Name /*filepath.Base(args.cipherdir)*/)
fido2HmacSalt = cryptocore.RandBytes(32)
password = fido2.Secret(args.fido2, fido2CredentialID, fido2HmacSalt)
// overwrite user to match fido2Name
args.user = fido2Name
} else {
// normal password entry
password, err = readpassword.Twice([]string(args.extpass), []string(args.passfile))
if err != nil {
tlog.Fatal.Println(err)
os.Exit(exitcodes.ReadPassword)
}
fido2Name = ""
fido2CredentialID = nil
fido2HmacSalt = nil
}
creator := tlog.ProgramName + " " + GitVersion
err = configfile.Create(&configfile.CreateArgs{
Filename: args.config,
User: args.user,
Password: password,
PlaintextNames: args.plaintextnames,
LogN: args.scryptn,
Creator: creator,
AESSIV: args.aessiv,
Fido2Name: fido2Name,
Fido2CredentialID: fido2CredentialID,
Fido2HmacSalt: fido2HmacSalt,
DeterministicNames: args.deterministic_names,
Expand Down
Loading