]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/rand for Windows
authorPeter Mundy <go.peter.90@gmail.com>
Mon, 12 Jul 2010 23:37:53 +0000 (16:37 -0700)
committerRuss Cox <rsc@golang.org>
Mon, 12 Jul 2010 23:37:53 +0000 (16:37 -0700)
R=rsc, brainman
CC=golang-dev
https://golang.org/cl/1773041

src/pkg/crypto/rand/Makefile
src/pkg/crypto/rand/rand.go
src/pkg/crypto/rand/rand_unix.go [new file with mode: 0644]
src/pkg/crypto/rand/rand_windows.go [new file with mode: 0755]
src/pkg/crypto/rsa/pkcs1v15_test.go
src/pkg/crypto/rsa/rsa_test.go
src/pkg/crypto/x509/x509_test.go
src/pkg/syscall/syscall_windows.go
src/pkg/syscall/zsyscall_windows_386.go
src/pkg/syscall/ztypes_windows_386.go

index 0e7a5536c35eb23aed3c2b319d7b75d8f8841d2e..21812598ccfb52d3e54333bccc205c024c3382d2 100644 (file)
@@ -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
index 01c30316bd2eaaccea7e8309940e59e39ba853e3..42d9da0efb00004cfe1f2b2fca4c61f67318eaa6 100644 (file)
 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 (file)
index 0000000..d8db6f2
--- /dev/null
@@ -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 (executable)
index 0000000..9bab2cb
--- /dev/null
@@ -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
+}
index bfc12be28500c0654e6c2e547a6b71398ae0ed12..9a4da232f0b2671136987148f711d93f00bc528f 100644 (file)
@@ -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)
        }
index 172173900feddeff66badb71c39c005fae83c1ca..66c24459a55c469213ca1b2bab88566e2e076d1b 100644 (file)
@@ -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 {
index 23ce1ad11fecc5f3cbade0fe43463f27bb8282c9..fa87fe26abcbe5bbef5092cc1a546f29ceebd4a6 100644 (file)
@@ -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
index 86badb8e939033198edd34bb01f22c6cbc37a722..a7f03add440caa80e279253edf5b3dcf84d7e8bd 100644 (file)
@@ -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
 
index be5dd031c80db9709a0a47eb0ca4b0cea4a1cb24..55f26734d0952b7d37451b4e0f422831b8734a9c 100644 (file)
@@ -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)
index 315a8ac2105cba83e918b5c7f2e9e29d41d08aca..4d3507868526c211cf99448d4c34218d6e4e4134 100644 (file)
@@ -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