Commit 67e1d400 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

crypto/rand: use getrandom system call on Linux

Adds internal/syscall package.

Fixes #8520

LGTM=r, agl
R=agl, rsc, r
CC=golang-codereviews, iant
https://golang.org/cl/123260044
parent 1837419f
// 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
}
...@@ -20,6 +20,8 @@ import ( ...@@ -20,6 +20,8 @@ import (
"time" "time"
) )
const urandomDevice = "/dev/urandom"
// Easy implementation: read from /dev/urandom. // Easy implementation: read from /dev/urandom.
// This is sufficient on Linux, OS X, and FreeBSD. // This is sufficient on Linux, OS X, and FreeBSD.
...@@ -27,7 +29,7 @@ func init() { ...@@ -27,7 +29,7 @@ func init() {
if runtime.GOOS == "plan9" { if runtime.GOOS == "plan9" {
Reader = newReader(nil) Reader = newReader(nil)
} else { } else {
Reader = &devReader{name: "/dev/urandom"} Reader = &devReader{name: urandomDevice}
} }
} }
...@@ -38,7 +40,14 @@ type devReader struct { ...@@ -38,7 +40,14 @@ type devReader struct {
mu sync.Mutex 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) { 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() r.mu.Lock()
defer r.mu.Unlock() defer r.mu.Unlock()
if r.f == nil { if r.f == nil {
......
...@@ -279,7 +279,7 @@ var pkgDeps = map[string][]string{ ...@@ -279,7 +279,7 @@ var pkgDeps = map[string][]string{
// Random byte, number generation. // Random byte, number generation.
// This would be part of core crypto except that it imports // This would be part of core crypto except that it imports
// math/big, which imports fmt. // 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. // Mathematical crypto: dependencies on fmt (L4) and math/big.
// We could avoid some of the fmt, but math/big imports fmt anyway. // We could avoid some of the fmt, but math/big imports fmt anyway.
......
// 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
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment