Skip to content

Commit

Permalink
main: Add '-devrandom' commandline option
Browse files Browse the repository at this point in the history
Allows to use /dev/random for generating the master key instead of the
default Go implementation. When the kernel random generator has been
properly initialized both are considered equally secure, however:

* Versions of Go prior to 1.9 just fall back to /dev/urandom if the
  getrandom() syscall would be blocking (Go Bug #19274)

* Kernel versions prior to 3.17 do not support getrandom(), and there
  is no check if the random generator has been properly initialized
  before reading from /dev/urandom

This is especially useful for embedded hardware with low-entroy. Please
note that generation of the master key might block indefinitely if the
kernel cannot harvest enough entropy.
  • Loading branch information
slackner authored and rfjakob committed Nov 21, 2017
1 parent 1b0426b commit f3c777d
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 7 deletions.
7 changes: 7 additions & 0 deletions Documentation/MANPAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ be suitable.
#### -d, -debug
Enable debug output

#### -devrandom
Use /dev/random for generating the master key instead of the default Go
implementation. This is especially useful on embedded systems with Go versions
prior to 1.9, which fall back to weak random data when the getrandom syscall
is blocking. Using this option can block indefinitely when the kernel cannot
harvest enough entropy.

#### -extpass string
Use an external program (like ssh-askpass) for the password prompt.
The program should return the password on stdout, a trailing newline is
Expand Down
3 changes: 2 additions & 1 deletion cli_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type argContainer struct {
plaintextnames, quiet, nosyslog, wpanic,
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64,
noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info,
sharedstorage bool
sharedstorage, devrandom bool
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
// Configuration file name override
Expand Down Expand Up @@ -132,6 +132,7 @@ func parseCliOpts() (args argContainer) {
flagSet.BoolVar(&args.hh, "hh", false, "Show this long help text")
flagSet.BoolVar(&args.info, "info", false, "Display information about CIPHERDIR")
flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer")
flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key")
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
Expand Down
2 changes: 1 addition & 1 deletion init_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func initDir(args *argContainer) {
password := readpassword.Twice(args.extpass)
readpassword.CheckTrailingGarbage()
creator := tlog.ProgramName + " " + GitVersion
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv)
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv, args.devrandom)
if err != nil {
tlog.Fatal.Println(err)
os.Exit(exitcodes.WriteConf)
Expand Down
26 changes: 24 additions & 2 deletions internal/configfile/config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ package configfile
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"

"github.com/rfjakob/gocryptfs/internal/contentenc"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
Expand Down Expand Up @@ -47,10 +49,25 @@ 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
}

// CreateConfFile - create a new config with a random key encrypted with
// "password" and write it to "filename".
// Uses scrypt with cost parameter logN.
func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, aessiv bool) error {
func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, aessiv bool, devrandom bool) error {
var cf ConfFile
cf.filename = filename
cf.Creator = creator
Expand All @@ -72,7 +89,12 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
}

// Generate new random master key
key := cryptocore.RandBytes(cryptocore.KeyLen)
var key []byte
if devrandom {
key = randBytesDevRandom(cryptocore.KeyLen)
} else {
key = cryptocore.RandBytes(cryptocore.KeyLen)
}

// Encrypt it using the password
// This sets ScryptObject and EncryptedKey
Expand Down
13 changes: 10 additions & 3 deletions internal/configfile/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestLoadV2StrangeFeature(t *testing.T) {
}

func TestCreateConfDefault(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false)
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -80,8 +80,15 @@ func TestCreateConfDefault(t *testing.T) {
}
}

func TestCreateConfDevRandom(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false, true)
if err != nil {
t.Fatal(err)
}
}

func TestCreateConfPlaintextnames(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", true, 10, "test", false)
err := CreateConfFile("config_test/tmp.conf", "test", true, 10, "test", false, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -102,7 +109,7 @@ func TestCreateConfPlaintextnames(t *testing.T) {

// Reverse mode uses AESSIV
func TestCreateConfFileAESSIV(t *testing.T) {
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true)
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true, false)
if err != nil {
t.Fatal(err)
}
Expand Down
5 changes: 5 additions & 0 deletions tests/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func TestInit(t *testing.T) {
}
}

// Test -init with -devrandom flag
func TestInitDevRandom(t *testing.T) {
test_helpers.InitFS(t, "-devrandom")
}

// Test -init with -aessiv
func TestInitAessiv(t *testing.T) {
dir := test_helpers.InitFS(t, "-aessiv")
Expand Down

0 comments on commit f3c777d

Please sign in to comment.