]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/rand: use getrandom system call on Linux
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 12 Aug 2014 21:35:27 +0000 (14:35 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 12 Aug 2014 21:35:27 +0000 (14:35 -0700)
Adds internal/syscall package.

Fixes #8520

LGTM=r, agl
R=agl, rsc, r
CC=golang-codereviews, iant
https://golang.org/cl/123260044

src/pkg/crypto/rand/rand_linux.go [new file with mode: 0644]
src/pkg/crypto/rand/rand_unix.go
src/pkg/go/build/deps_test.go
src/pkg/internal/syscall/getrandom_linux.go [new file with mode: 0644]

diff --git a/src/pkg/crypto/rand/rand_linux.go b/src/pkg/crypto/rand/rand_linux.go
new file mode 100644 (file)
index 0000000..8cb59c7
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package rand
+
+import (
+       "internal/syscall"
+       "sync"
+)
+
+func init() {
+       altGetRandom = getRandomLinux
+}
+
+var (
+       once       sync.Once
+       useSyscall bool
+)
+
+func pickStrategy() {
+       // Test whether we should use the system call or /dev/urandom.
+       // We'll fall back to urandom if:
+       // - the kernel is too old (before 3.17)
+       // - the machine has no entropy available (early boot + no hardware
+       //   entropy source?) and we want to avoid blocking later.
+       var buf [1]byte
+       n, err := syscall.GetRandom(buf[:], syscall.GRND_NONBLOCK)
+       useSyscall = n == 1 && err == nil
+}
+
+func getRandomLinux(p []byte) (ok bool) {
+       once.Do(pickStrategy)
+       if !useSyscall {
+               return false
+       }
+       n, err := syscall.GetRandom(p, 0)
+       return n == len(p) && err == nil
+}
index 1e741fda193b111eaa421f200291677538560439..62d0fbdb350c4d7013186893b3d065e1ce5b8a02 100644 (file)
@@ -20,6 +20,8 @@ import (
        "time"
 )
 
+const urandomDevice = "/dev/urandom"
+
 // Easy implementation: read from /dev/urandom.
 // This is sufficient on Linux, OS X, and FreeBSD.
 
@@ -27,7 +29,7 @@ func init() {
        if runtime.GOOS == "plan9" {
                Reader = newReader(nil)
        } else {
-               Reader = &devReader{name: "/dev/urandom"}
+               Reader = &devReader{name: urandomDevice}
        }
 }
 
@@ -38,7 +40,14 @@ type devReader struct {
        mu   sync.Mutex
 }
 
+// altGetRandom if non-nil specifies an OS-specific function to get
+// urandom-style randomness.
+var altGetRandom func([]byte) (ok bool)
+
 func (r *devReader) Read(b []byte) (n int, err error) {
+       if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) {
+               return len(b), nil
+       }
        r.mu.Lock()
        defer r.mu.Unlock()
        if r.f == nil {
index 99b985b51d99918332b6a8c9f3d86afdae1a66c6..2a7173ba4c42081c1b5fd9b68fde3f3db6d5cae2 100644 (file)
@@ -279,7 +279,7 @@ var pkgDeps = map[string][]string{
        // Random byte, number generation.
        // This would be part of core crypto except that it imports
        // math/big, which imports fmt.
-       "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall"},
+       "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall"},
 
        // Mathematical crypto: dependencies on fmt (L4) and math/big.
        // We could avoid some of the fmt, but math/big imports fmt anyway.
diff --git a/src/pkg/internal/syscall/getrandom_linux.go b/src/pkg/internal/syscall/getrandom_linux.go
new file mode 100644 (file)
index 0000000..1c586ec
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+import (
+       "runtime"
+       "sync/atomic"
+       stdsyscall "syscall"
+       "unsafe"
+)
+
+var randomTrap = map[string]uintptr{
+       "amd64": 318,
+       "386":   355,
+}[runtime.GOARCH]
+
+var randomUnsupported int32 // atomic
+
+// GetRandomFlag is a flag supported by the getrandom system call.
+type GetRandomFlag uintptr
+
+const (
+       // GRND_NONBLOCK means return EAGAIN rather than blocking.
+       GRND_NONBLOCK GetRandomFlag = 0x0001
+
+       // GRND_RANDOM means use the /dev/random pool instead of /dev/urandom.
+       GRND_RANDOM GetRandomFlag = 0x0002
+)
+
+// GetRandom calls the Linux getrandom system call.
+// See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6e9d6f38894798696f23c8084ca7edbf16ee895
+func GetRandom(p []byte, flags GetRandomFlag) (n int, err error) {
+       if randomTrap == 0 {
+               return 0, stdsyscall.ENOSYS
+       }
+       if len(p) == 0 {
+               return 0, nil
+       }
+       if atomic.LoadInt32(&randomUnsupported) != 0 {
+               return 0, stdsyscall.ENOSYS
+       }
+       r1, _, errno := stdsyscall.Syscall(randomTrap,
+               uintptr(unsafe.Pointer(&p[0])),
+               uintptr(len(p)),
+               uintptr(flags))
+       if errno != 0 {
+               if errno == stdsyscall.ENOSYS {
+                       atomic.StoreInt32(&randomUnsupported, 1)
+               }
+               return 0, errno
+       }
+       return int(r1), nil
+}