From ccd28e8eb6f81f21093deb730ea70982cb381514 Mon Sep 17 00:00:00 2001 From: Peter Mundy Date: Mon, 12 Jul 2010 16:37:53 -0700 Subject: [PATCH] crypto/rand for Windows R=rsc, brainman CC=golang-dev https://golang.org/cl/1773041 --- src/pkg/crypto/rand/Makefile | 17 ++++ src/pkg/crypto/rand/rand.go | 113 +-------------------- src/pkg/crypto/rand/rand_unix.go | 124 ++++++++++++++++++++++++ src/pkg/crypto/rand/rand_windows.go | 42 ++++++++ src/pkg/crypto/rsa/pkcs1v15_test.go | 18 ++-- src/pkg/crypto/rsa/rsa_test.go | 18 ++-- src/pkg/crypto/x509/x509_test.go | 9 +- src/pkg/syscall/syscall_windows.go | 3 + src/pkg/syscall/zsyscall_windows_386.go | 37 +++++++ src/pkg/syscall/ztypes_windows_386.go | 28 ++++++ 10 files changed, 268 insertions(+), 141 deletions(-) create mode 100644 src/pkg/crypto/rand/rand_unix.go create mode 100755 src/pkg/crypto/rand/rand_windows.go 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 -- 2.48.1