Browse Source

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
Jakob Borg 9 years ago
parent
commit
e6b78e5d56

+ 2 - 2
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

+ 2 - 3
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()

+ 2 - 2
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...)

+ 3 - 3
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

+ 2 - 1
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)
 	}
 }
 

+ 22 - 11
lib/util/random.go → 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,

+ 4 - 4
lib/util/random_test.go → 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

+ 1 - 1
lib/util/securesource.go → 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"

+ 1 - 1
lib/util/securesource_test.go → 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"
 

+ 3 - 3
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,
 		},