aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Mundy <go.peter.90@gmail.com>2010-07-12 16:37:53 -0700
committerRuss Cox <rsc@golang.org>2010-07-12 16:37:53 -0700
commitccd28e8eb6f81f21093deb730ea70982cb381514 (patch)
tree0daf2655cffae43528c07f5b87401cf32f3678fb
parentc9f83372d87fca87873116aba2efaabb3137375b (diff)
downloadgo-ccd28e8eb6f81f21093deb730ea70982cb381514.tar.gz
go-ccd28e8eb6f81f21093deb730ea70982cb381514.zip
crypto/rand for Windows
R=rsc, brainman CC=golang-dev https://golang.org/cl/1773041
-rw-r--r--src/pkg/crypto/rand/Makefile17
-rw-r--r--src/pkg/crypto/rand/rand.go113
-rw-r--r--src/pkg/crypto/rand/rand_unix.go124
-rwxr-xr-xsrc/pkg/crypto/rand/rand_windows.go42
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15_test.go18
-rw-r--r--src/pkg/crypto/rsa/rsa_test.go18
-rw-r--r--src/pkg/crypto/x509/x509_test.go9
-rw-r--r--src/pkg/syscall/syscall_windows.go3
-rw-r--r--src/pkg/syscall/zsyscall_windows_386.go37
-rw-r--r--src/pkg/syscall/ztypes_windows_386.go28
10 files changed, 268 insertions, 141 deletions
diff --git a/src/pkg/crypto/rand/Makefile b/src/pkg/crypto/rand/Makefile
index 0e7a5536c3..21812598cc 100644
--- a/src/pkg/crypto/rand/Makefile
+++ b/src/pkg/crypto/rand/Makefile
@@ -9,4 +9,21 @@ TARG=crypto/rand
GOFILES=\
rand.go\
+GOFILES_freebsd=\
+ rand_unix.go\
+
+GOFILES_darwin=\
+ rand_unix.go\
+
+GOFILES_linux=\
+ rand_unix.go\
+
+GOFILES_nacl=\
+ rand_unix.go\
+
+GOFILES_windows=\
+ rand_windows.go\
+
+GOFILES+=$(GOFILES_$(GOOS))
+
include ../../../Make.pkg
diff --git a/src/pkg/crypto/rand/rand.go b/src/pkg/crypto/rand/rand.go
index 01c30316bd..42d9da0efb 100644
--- a/src/pkg/crypto/rand/rand.go
+++ b/src/pkg/crypto/rand/rand.go
@@ -7,124 +7,15 @@
package rand
import (
- "crypto/aes"
"io"
"os"
- "sync"
- "time"
)
// Reader is a global, shared instance of a cryptographically
// strong pseudo-random generator.
+// On Unix-like systems, Reader reads from /dev/urandom.
+// On Windows systems, Reader uses the CryptGenRandom API.
var Reader io.Reader
// Read is a helper function that calls Reader.Read.
func Read(b []byte) (n int, err os.Error) { return Reader.Read(b) }
-
-// Easy implementation: read from /dev/urandom.
-// This is sufficient on Linux, OS X, and FreeBSD.
-
-func init() { Reader = &devReader{name: "/dev/urandom"} }
-
-// A devReader satisfies reads by reading the file named name.
-type devReader struct {
- name string
- f *os.File
- mu sync.Mutex
-}
-
-func (r *devReader) Read(b []byte) (n int, err os.Error) {
- r.mu.Lock()
- if r.f == nil {
- f, err := os.Open(r.name, os.O_RDONLY, 0)
- if f == nil {
- return 0, err
- }
- r.f = f
- }
- r.mu.Unlock()
- return r.f.Read(b)
-}
-
-// Alternate pseudo-random implementation for use on
-// systems without a reliable /dev/urandom. So far we
-// haven't needed it.
-
-// newReader returns a new pseudorandom generator that
-// seeds itself by reading from entropy. If entropy == nil,
-// the generator seeds itself by reading from the system's
-// random number generator, typically /dev/random.
-// The Read method on the returned reader always returns
-// the full amount asked for, or else it returns an error.
-//
-// The generator uses the X9.31 algorithm with AES-128,
-// reseeding after every 1 MB of generated data.
-func newReader(entropy io.Reader) io.Reader {
- if entropy == nil {
- entropy = &devReader{name: "/dev/random"}
- }
- return &reader{entropy: entropy}
-}
-
-type reader struct {
- mu sync.Mutex
- budget int // number of bytes that can be generated
- cipher *aes.Cipher
- entropy io.Reader
- time, seed, dst, key [aes.BlockSize]byte
-}
-
-func (r *reader) Read(b []byte) (n int, err os.Error) {
- r.mu.Lock()
- defer r.mu.Unlock()
- n = len(b)
-
- for len(b) > 0 {
- if r.budget == 0 {
- _, err := io.ReadFull(r.entropy, r.seed[0:])
- if err != nil {
- return n - len(b), err
- }
- _, err = io.ReadFull(r.entropy, r.key[0:])
- if err != nil {
- return n - len(b), err
- }
- r.cipher, err = aes.NewCipher(r.key[0:])
- if err != nil {
- return n - len(b), err
- }
- r.budget = 1 << 20 // reseed after generating 1MB
- }
- r.budget -= aes.BlockSize
-
- // ANSI X9.31 (== X9.17) algorithm, but using AES in place of 3DES.
- //
- // single block:
- // t = encrypt(time)
- // dst = encrypt(t^seed)
- // seed = encrypt(t^dst)
- ns := time.Nanoseconds()
- r.time[0] = byte(ns >> 56)
- r.time[1] = byte(ns >> 48)
- r.time[2] = byte(ns >> 40)
- r.time[3] = byte(ns >> 32)
- r.time[4] = byte(ns >> 24)
- r.time[5] = byte(ns >> 16)
- r.time[6] = byte(ns >> 8)
- r.time[7] = byte(ns)
- r.cipher.Encrypt(r.time[0:], r.time[0:])
- for i := 0; i < aes.BlockSize; i++ {
- r.dst[i] = r.time[i] ^ r.seed[i]
- }
- r.cipher.Encrypt(r.dst[0:], r.dst[0:])
- for i := 0; i < aes.BlockSize; i++ {
- r.seed[i] = r.time[i] ^ r.dst[i]
- }
- r.cipher.Encrypt(r.seed[0:], r.seed[0:])
-
- m := copy(b, r.dst[0:])
- b = b[m:]
- }
-
- return n, nil
-}
diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go
new file mode 100644
index 0000000000..d8db6f2a04
--- /dev/null
+++ b/src/pkg/crypto/rand/rand_unix.go
@@ -0,0 +1,124 @@
+// Copyright 2010 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.
+
+// Unix cryptographically secure pseudorandom number
+// generator.
+
+package rand
+
+import (
+ "crypto/aes"
+ "io"
+ "os"
+ "sync"
+ "time"
+)
+
+// Easy implementation: read from /dev/urandom.
+// This is sufficient on Linux, OS X, and FreeBSD.
+
+func init() { Reader = &devReader{name: "/dev/urandom"} }
+
+// A devReader satisfies reads by reading the file named name.
+type devReader struct {
+ name string
+ f *os.File
+ mu sync.Mutex
+}
+
+func (r *devReader) Read(b []byte) (n int, err os.Error) {
+ r.mu.Lock()
+ if r.f == nil {
+ f, err := os.Open(r.name, os.O_RDONLY, 0)
+ if f == nil {
+ return 0, err
+ }
+ r.f = f
+ }
+ r.mu.Unlock()
+ return r.f.Read(b)
+}
+
+// Alternate pseudo-random implementation for use on
+// systems without a reliable /dev/urandom. So far we
+// haven't needed it.
+
+// newReader returns a new pseudorandom generator that
+// seeds itself by reading from entropy. If entropy == nil,
+// the generator seeds itself by reading from the system's
+// random number generator, typically /dev/random.
+// The Read method on the returned reader always returns
+// the full amount asked for, or else it returns an error.
+//
+// The generator uses the X9.31 algorithm with AES-128,
+// reseeding after every 1 MB of generated data.
+func newReader(entropy io.Reader) io.Reader {
+ if entropy == nil {
+ entropy = &devReader{name: "/dev/random"}
+ }
+ return &reader{entropy: entropy}
+}
+
+type reader struct {
+ mu sync.Mutex
+ budget int // number of bytes that can be generated
+ cipher *aes.Cipher
+ entropy io.Reader
+ time, seed, dst, key [aes.BlockSize]byte
+}
+
+func (r *reader) Read(b []byte) (n int, err os.Error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ n = len(b)
+
+ for len(b) > 0 {
+ if r.budget == 0 {
+ _, err := io.ReadFull(r.entropy, r.seed[0:])
+ if err != nil {
+ return n - len(b), err
+ }
+ _, err = io.ReadFull(r.entropy, r.key[0:])
+ if err != nil {
+ return n - len(b), err
+ }
+ r.cipher, err = aes.NewCipher(r.key[0:])
+ if err != nil {
+ return n - len(b), err
+ }
+ r.budget = 1 << 20 // reseed after generating 1MB
+ }
+ r.budget -= aes.BlockSize
+
+ // ANSI X9.31 (== X9.17) algorithm, but using AES in place of 3DES.
+ //
+ // single block:
+ // t = encrypt(time)
+ // dst = encrypt(t^seed)
+ // seed = encrypt(t^dst)
+ ns := time.Nanoseconds()
+ r.time[0] = byte(ns >> 56)
+ r.time[1] = byte(ns >> 48)
+ r.time[2] = byte(ns >> 40)
+ r.time[3] = byte(ns >> 32)
+ r.time[4] = byte(ns >> 24)
+ r.time[5] = byte(ns >> 16)
+ r.time[6] = byte(ns >> 8)
+ r.time[7] = byte(ns)
+ r.cipher.Encrypt(r.time[0:], r.time[0:])
+ for i := 0; i < aes.BlockSize; i++ {
+ r.dst[i] = r.time[i] ^ r.seed[i]
+ }
+ r.cipher.Encrypt(r.dst[0:], r.dst[0:])
+ for i := 0; i < aes.BlockSize; i++ {
+ r.seed[i] = r.time[i] ^ r.dst[i]
+ }
+ r.cipher.Encrypt(r.seed[0:], r.seed[0:])
+
+ m := copy(b, r.dst[0:])
+ b = b[m:]
+ }
+
+ return n, nil
+}
diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go
new file mode 100755
index 0000000000..9bab2cba88
--- /dev/null
+++ b/src/pkg/crypto/rand/rand_windows.go
@@ -0,0 +1,42 @@
+// Copyright 2010 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.
+
+// Windows cryptographically secure pseudorandom number
+// generator.
+
+package rand
+
+import (
+ "os"
+ "sync"
+ "syscall"
+)
+
+// Implemented by using Windows CryptoAPI 2.0.
+
+func init() { Reader = &rngReader{} }
+
+// A rngReader satisfies reads by reading from the Windows CryptGenRandom API.
+type rngReader struct {
+ prov uint32
+ mu sync.Mutex
+}
+
+func (r *rngReader) Read(b []byte) (n int, err os.Error) {
+ r.mu.Lock()
+ if r.prov == 0 {
+ const provType = syscall.PROV_RSA_FULL
+ const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT
+ ok, errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags)
+ if !ok {
+ return 0, os.NewSyscallError("CryptAcquireContext", errno)
+ }
+ }
+ r.mu.Unlock()
+ ok, errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0])
+ if !ok {
+ return 0, os.NewSyscallError("CryptGenRandom", errno)
+ }
+ return len(b), nil
+}
diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go
index bfc12be285..9a4da232f0 100644
--- a/src/pkg/crypto/rsa/pkcs1v15_test.go
+++ b/src/pkg/crypto/rsa/pkcs1v15_test.go
@@ -7,10 +7,10 @@ package rsa
import (
"big"
"bytes"
+ "crypto/rand"
"crypto/sha1"
"encoding/base64"
"encoding/hex"
- "os"
"io"
"testing"
"testing/quick"
@@ -63,10 +63,7 @@ func TestDecryptPKCS1v15(t *testing.T) {
}
func TestEncryptPKCS1v15(t *testing.T) {
- urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0)
- if err != nil {
- t.Errorf("Failed to open /dev/urandom")
- }
+ random := rand.Reader
k := (rsaPrivateKey.N.BitLen() + 7) / 8
tryEncryptDecrypt := func(in []byte, blind bool) bool {
@@ -74,7 +71,7 @@ func TestEncryptPKCS1v15(t *testing.T) {
in = in[0 : k-11]
}
- ciphertext, err := EncryptPKCS1v15(urandom, &rsaPrivateKey.PublicKey, in)
+ ciphertext, err := EncryptPKCS1v15(random, &rsaPrivateKey.PublicKey, in)
if err != nil {
t.Errorf("error encrypting: %s", err)
return false
@@ -84,7 +81,7 @@ func TestEncryptPKCS1v15(t *testing.T) {
if !blind {
rand = nil
} else {
- rand = urandom
+ rand = random
}
plaintext, err := DecryptPKCS1v15(rand, rsaPrivateKey, ciphertext)
if err != nil {
@@ -137,13 +134,10 @@ func TestEncryptPKCS1v15SessionKey(t *testing.T) {
}
func TestNonZeroRandomBytes(t *testing.T) {
- urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0)
- if err != nil {
- t.Errorf("Failed to open /dev/urandom")
- }
+ random := rand.Reader
b := make([]byte, 512)
- err = nonZeroRandomBytes(b, urandom)
+ err := nonZeroRandomBytes(b, random)
if err != nil {
t.Errorf("returned error: %s", err)
}
diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go
index 172173900f..66c24459a5 100644
--- a/src/pkg/crypto/rsa/rsa_test.go
+++ b/src/pkg/crypto/rsa/rsa_test.go
@@ -7,18 +7,15 @@ package rsa
import (
"big"
"bytes"
+ "crypto/rand"
"crypto/sha1"
- "os"
"testing"
)
func TestKeyGeneration(t *testing.T) {
- urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0)
- if err != nil {
- t.Errorf("failed to open /dev/urandom")
- }
+ random := rand.Reader
- priv, err := GenerateKey(urandom, 1024)
+ priv, err := GenerateKey(random, 1024)
if err != nil {
t.Errorf("failed to generate key")
}
@@ -33,7 +30,7 @@ func TestKeyGeneration(t *testing.T) {
t.Errorf("got:%v, want:%v (%s)", m2, m, priv)
}
- m3, err := decrypt(urandom, priv, c)
+ m3, err := decrypt(random, priv, c)
if err != nil {
t.Errorf("error while decrypting (blind): %s", err)
}
@@ -76,10 +73,7 @@ func TestEncryptOAEP(t *testing.T) {
}
func TestDecryptOAEP(t *testing.T) {
- urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0)
- if err != nil {
- t.Errorf("Failed to open /dev/urandom")
- }
+ random := rand.Reader
sha1 := sha1.New()
n := new(big.Int)
@@ -98,7 +92,7 @@ func TestDecryptOAEP(t *testing.T) {
}
// Decrypt with blinding.
- out, err = DecryptOAEP(sha1, urandom, &private, message.out, nil)
+ out, err = DecryptOAEP(sha1, random, &private, message.out, nil)
if err != nil {
t.Errorf("#%d,%d (blind) error: %s", i, j, err)
} else if bytes.Compare(out, message.in) != 0 {
diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go
index 23ce1ad11f..fa87fe26ab 100644
--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -6,10 +6,10 @@ package x509
import (
"big"
+ "crypto/rand"
"crypto/rsa"
"encoding/hex"
"encoding/pem"
- "os"
"reflect"
"testing"
"time"
@@ -145,10 +145,7 @@ var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be13
"36dcd585d6ace53f546f961e05af"
func TestCreateSelfSignedCertificate(t *testing.T) {
- urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0)
- if err != nil {
- t.Errorf("failed to open /dev/urandom")
- }
+ random := rand.Reader
block, _ := pem.Decode([]byte(pemPrivateKey))
priv, err := ParsePKCS1PrivateKey(block.Bytes)
@@ -174,7 +171,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
DNSNames: []string{"test.example.com"},
}
- derBytes, err := CreateCertificate(urandom, &template, &template, &priv.PublicKey, priv)
+ derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv)
if err != nil {
t.Errorf("Failed to create certificate: %s", err)
return
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index 86badb8e93..a7f03add44 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -132,6 +132,9 @@ func getSysProcAddr(m uint32, pname string) uintptr {
//sys CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int)
//sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (ok bool, errno int)
//sys GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) = GetTempPathW
+//sys CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (ok bool, errno int) = advapi32.CryptAcquireContextW
+//sys CryptReleaseContext(provhandle uint32, flags uint32) (ok bool, errno int) = advapi32.CryptReleaseContext
+//sys CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (ok bool, errno int) = advapi32.CryptGenRandom
// syscall interface implementation for other packages
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index be5dd031c8..55f26734d0 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -7,6 +7,7 @@ import "unsafe"
var (
modkernel32 = loadDll("kernel32.dll")
+ modadvapi32 = loadDll("advapi32.dll")
modwsock32 = loadDll("wsock32.dll")
modws2_32 = loadDll("ws2_32.dll")
@@ -41,6 +42,9 @@ var (
procCreateIoCompletionPort = getSysProcAddr(modkernel32, "CreateIoCompletionPort")
procGetQueuedCompletionStatus = getSysProcAddr(modkernel32, "GetQueuedCompletionStatus")
procGetTempPathW = getSysProcAddr(modkernel32, "GetTempPathW")
+ procCryptAcquireContextW = getSysProcAddr(modadvapi32, "CryptAcquireContextW")
+ procCryptReleaseContext = getSysProcAddr(modadvapi32, "CryptReleaseContext")
+ procCryptGenRandom = getSysProcAddr(modadvapi32, "CryptGenRandom")
procWSAStartup = getSysProcAddr(modwsock32, "WSAStartup")
procWSACleanup = getSysProcAddr(modwsock32, "WSACleanup")
procsocket = getSysProcAddr(modwsock32, "socket")
@@ -387,6 +391,39 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) {
return
}
+func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (ok bool, errno int) {
+ r0, _, e1 := Syscall6(procCryptAcquireContextW, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0)
+ ok = bool(r0 != 0)
+ if !ok {
+ errno = int(e1)
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CryptReleaseContext(provhandle uint32, flags uint32) (ok bool, errno int) {
+ r0, _, e1 := Syscall(procCryptReleaseContext, uintptr(provhandle), uintptr(flags), 0)
+ ok = bool(r0 != 0)
+ if !ok {
+ errno = int(e1)
+ } else {
+ errno = 0
+ }
+ return
+}
+
+func CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (ok bool, errno int) {
+ r0, _, e1 := Syscall(procCryptGenRandom, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf)))
+ ok = bool(r0 != 0)
+ if !ok {
+ errno = int(e1)
+ } else {
+ errno = 0
+ }
+ return
+}
+
func WSAStartup(verreq uint32, data *WSAData) (sockerrno int) {
r0, _, _ := Syscall(procWSAStartup, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
sockerrno = int(r0)
diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go
index 315a8ac210..4d35078685 100644
--- a/src/pkg/syscall/ztypes_windows_386.go
+++ b/src/pkg/syscall/ztypes_windows_386.go
@@ -88,6 +88,34 @@ const (
WAIT_TIMEOUT = 258
)
+const (
+ // wincrypt.h
+ PROV_RSA_FULL = 1
+ PROV_RSA_SIG = 2
+ PROV_DSS = 3
+ PROV_FORTEZZA = 4
+ PROV_MS_EXCHANGE = 5
+ PROV_SSL = 6
+ PROV_RSA_SCHANNEL = 12
+ PROV_DSS_DH = 13
+ PROV_EC_ECDSA_SIG = 14
+ PROV_EC_ECNRA_SIG = 15
+ PROV_EC_ECDSA_FULL = 16
+ PROV_EC_ECNRA_FULL = 17
+ PROV_DH_SCHANNEL = 18
+ PROV_SPYRUS_LYNKS = 20
+ PROV_RNG = 21
+ PROV_INTEL_SEC = 22
+ PROV_REPLACE_OWF = 23
+ PROV_RSA_AES = 24
+ CRYPT_VERIFYCONTEXT = 0xF0000000
+ CRYPT_NEWKEYSET = 0x00000008
+ CRYPT_DELETEKEYSET = 0x00000010
+ CRYPT_MACHINE_KEYSET = 0x00000020
+ CRYPT_SILENT = 0x00000040
+ CRYPT_DEFAULT_CONTAINER_OPTIONAL = 0x00000080
+)
+
// Types
type _C_short int16