1
0
mirror of https://github.com/golang/go synced 2024-11-19 21:04:43 -07:00

crypto/rand for Windows

R=rsc, brainman
CC=golang-dev
https://golang.org/cl/1773041
This commit is contained in:
Peter Mundy 2010-07-12 16:37:53 -07:00 committed by Russ Cox
parent c9f83372d8
commit ccd28e8eb6
10 changed files with 268 additions and 141 deletions

View 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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View 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)
}

View 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 {

View 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

View 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

View 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)

View 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