From e6b78e5d56b1a426d08506c85075afede88e15fa Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Thu, 26 May 2016 07:02:56 +0000 Subject: [PATCH] lib/rand: Break out random functions into separate package The intention for this package is to provide a combination of the security of crypto/rand and the convenience of math/rand. It should be the first choice of random data unless ultimate performance is required and the usage is provably irrelevant from a security standpoint. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3186 --- cmd/syncthing/gui.go | 4 +-- cmd/syncthing/gui_auth.go | 5 ++-- cmd/syncthing/gui_csrf.go | 4 +-- cmd/syncthing/main.go | 6 ++--- lib/config/config.go | 3 ++- lib/{util => rand}/random.go | 33 ++++++++++++++++--------- lib/{util => rand}/random_test.go | 8 +++--- lib/{util => rand}/securesource.go | 2 +- lib/{util => rand}/securesource_test.go | 2 +- lib/tlsutil/tlsutil.go | 6 ++--- 10 files changed, 42 insertions(+), 31 deletions(-) rename lib/{util => rand}/random.go (69%) rename lib/{util => rand}/random_test.go (93%) rename lib/{util => rand}/securesource.go (99%) rename lib/{util => rand}/securesource_test.go (99%) diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 2f14dfb5..2a092e92 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -35,11 +35,11 @@ import ( "github.com/syncthing/syncthing/lib/model" "github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/stats" "github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/upgrade" - "github.com/syncthing/syncthing/lib/util" "github.com/vitrun/qart/qr" "golang.org/x/crypto/bcrypt" ) @@ -750,7 +750,7 @@ func (s *apiService) postSystemConfig(w http.ResponseWriter, r *http.Request) { if curAcc := s.cfg.Options().URAccepted; to.Options.URAccepted > curAcc { // UR was enabled to.Options.URAccepted = usageReportVersion - to.Options.URUniqueID = util.RandomString(8) + to.Options.URUniqueID = rand.String(8) } else if to.Options.URAccepted < curAcc { // UR was disabled to.Options.URAccepted = -1 diff --git a/cmd/syncthing/gui_auth.go b/cmd/syncthing/gui_auth.go index e062185a..b1e3e172 100644 --- a/cmd/syncthing/gui_auth.go +++ b/cmd/syncthing/gui_auth.go @@ -9,15 +9,14 @@ package main import ( "bytes" "encoding/base64" - "math/rand" "net/http" "strings" "time" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/events" + "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/sync" - "github.com/syncthing/syncthing/lib/util" "golang.org/x/crypto/bcrypt" ) @@ -114,7 +113,7 @@ func basicAuthAndSessionMiddleware(cookieName string, cfg config.GUIConfiguratio return passwordOK: - sessionid := util.RandomString(32) + sessionid := rand.String(32) sessionsMut.Lock() sessions[sessionid] = true sessionsMut.Unlock() diff --git a/cmd/syncthing/gui_csrf.go b/cmd/syncthing/gui_csrf.go index c17c394a..30bc8232 100644 --- a/cmd/syncthing/gui_csrf.go +++ b/cmd/syncthing/gui_csrf.go @@ -15,8 +15,8 @@ import ( "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/osutil" + "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/sync" - "github.com/syncthing/syncthing/lib/util" ) // csrfTokens is a list of valid tokens. It is sorted so that the most @@ -87,7 +87,7 @@ func validCsrfToken(token string) bool { } func newCsrfToken() string { - token := util.RandomString(32) + token := rand.String(32) csrfMut.Lock() csrfTokens = append([]string{token}, csrfTokens...) diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 767ca859..40f773a2 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -39,10 +39,10 @@ import ( "github.com/syncthing/syncthing/lib/model" "github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/symlinks" "github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/upgrade" - "github.com/syncthing/syncthing/lib/util" "github.com/thejerf/suture" ) @@ -761,7 +761,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) { if opts.URUniqueID == "" { // Previously the ID was generated from the node ID. We now need // to generate a new one. - opts.URUniqueID = util.RandomString(8) + opts.URUniqueID = rand.String(8) cfg.SetOptions(opts) cfg.Save() } @@ -947,7 +947,7 @@ func defaultConfig(myName string) config.Configuration { if !noDefaultFolder { l.Infoln("Default folder created and/or linked to new config") - folderID := util.RandomString(5) + "-" + util.RandomString(5) + folderID := rand.String(5) + "-" + rand.String(5) defaultFolder = config.NewFolderConfiguration(folderID, locations[locDefFolder]) defaultFolder.Label = "Default Folder (" + folderID + ")" defaultFolder.RescanIntervalS = 60 diff --git a/lib/config/config.go b/lib/config/config.go index 6116f164..aa8c1279 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/util" ) @@ -254,7 +255,7 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) { } if cfg.GUI.APIKey == "" { - cfg.GUI.APIKey = util.RandomString(32) + cfg.GUI.APIKey = rand.String(32) } } diff --git a/lib/util/random.go b/lib/rand/random.go similarity index 69% rename from lib/util/random.go rename to lib/rand/random.go index 78a73f11..78934655 100644 --- a/lib/util/random.go +++ b/lib/rand/random.go @@ -4,7 +4,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. -package util +// Package rand implements functions similar to math/rand in the standard +// library, but on top of a secure random number generator. +package rand import ( "crypto/md5" @@ -14,6 +16,9 @@ import ( mathRand "math/rand" ) +// Reader is the standard crypto/rand.Reader, re-exported for convenience +var Reader = cryptoRand.Reader + // randomCharset contains the characters that can make up a randomString(). const randomCharset = "2345679abcdefghijkmnopqrstuvwxyzACDEFGHJKLMNPQRSTUVWXYZ" @@ -26,15 +31,10 @@ var ( defaultSecureRand = mathRand.New(defaltSecureSource) ) -func init() { - // The default RNG should be seeded with something good. - mathRand.Seed(RandomInt64()) -} - -// RandomString returns a strongly random string of characters (taken from +// String returns a strongly random string of characters (taken from // randomCharset) of the specified length. The returned string contains ~5.8 // bits of entropy per character, due to the character set used. -func RandomString(l int) string { +func String(l int) string { bs := make([]byte, l) for i := range bs { bs[i] = randomCharset[defaultSecureRand.Intn(len(randomCharset))] @@ -42,14 +42,25 @@ func RandomString(l int) string { return string(bs) } -// RandomInt64 returns a strongly random int64, slowly -func RandomInt64() int64 { +// Int63 returns a strongly random int63 +func Int63() int64 { + return defaltSecureSource.Int63() +} + +// Int64 returns a strongly random int64 +func Int64() int64 { var bs [8]byte _, err := io.ReadFull(cryptoRand.Reader, bs[:]) if err != nil { panic("randomness failure: " + err.Error()) } - return SeedFromBytes(bs[:]) + return int64(binary.BigEndian.Uint64(bs[:])) +} + +// Intn returns, as an int, a non-negative strongly random number in [0,n). +// It panics if n <= 0. +func Intn(n int) int { + return defaultSecureRand.Intn(n) } // SeedFromBytes calculates a weak 64 bit hash from the given byte slice, diff --git a/lib/util/random_test.go b/lib/rand/random_test.go similarity index 93% rename from lib/util/random_test.go rename to lib/rand/random_test.go index 8d6b5f71..95647176 100644 --- a/lib/util/random_test.go +++ b/lib/rand/random_test.go @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. -package util +package rand import "testing" @@ -27,7 +27,7 @@ func TestSeedFromBytes(t *testing.T) { func TestRandomString(t *testing.T) { for _, l := range []int{0, 1, 2, 3, 4, 8, 42} { - s := RandomString(l) + s := String(l) if len(s) != l { t.Errorf("Incorrect length %d != %d", len(s), l) } @@ -35,7 +35,7 @@ func TestRandomString(t *testing.T) { strings := make([]string, 1000) for i := range strings { - strings[i] = RandomString(8) + strings[i] = String(8) for j := range strings { if i == j { continue @@ -50,7 +50,7 @@ func TestRandomString(t *testing.T) { func TestRandomInt64(t *testing.T) { ints := make([]int64, 1000) for i := range ints { - ints[i] = RandomInt64() + ints[i] = Int64() for j := range ints { if i == j { continue diff --git a/lib/util/securesource.go b/lib/rand/securesource.go similarity index 99% rename from lib/util/securesource.go rename to lib/rand/securesource.go index efafd39e..b2aec0f7 100644 --- a/lib/util/securesource.go +++ b/lib/rand/securesource.go @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. -package util +package rand import ( "bufio" diff --git a/lib/util/securesource_test.go b/lib/rand/securesource_test.go similarity index 99% rename from lib/util/securesource_test.go rename to lib/rand/securesource_test.go index 6aa9f086..22457b13 100644 --- a/lib/util/securesource_test.go +++ b/lib/rand/securesource_test.go @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. -package util +package rand import "testing" diff --git a/lib/tlsutil/tlsutil.go b/lib/tlsutil/tlsutil.go index 298b8356..0855f473 100644 --- a/lib/tlsutil/tlsutil.go +++ b/lib/tlsutil/tlsutil.go @@ -10,7 +10,6 @@ import ( "bufio" "crypto/ecdsa" "crypto/elliptic" - "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" @@ -19,10 +18,11 @@ import ( "fmt" "io" "math/big" - mr "math/rand" "net" "os" "time" + + "github.com/syncthing/syncthing/lib/rand" ) var ( @@ -48,7 +48,7 @@ func NewCertificate(certFile, keyFile, tlsDefaultCommonName string, tlsRSABits i notAfter := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC) template := x509.Certificate{ - SerialNumber: new(big.Int).SetInt64(mr.Int63()), + SerialNumber: new(big.Int).SetInt64(rand.Int63()), Subject: pkix.Name{ CommonName: tlsDefaultCommonName, },