From 517a6d6e5763d17405164cf4a3c114bf51e0d976 Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Sat, 1 Feb 2020 23:46:46 +0000 Subject: [PATCH] Handle error and retry for Crypto.rand --- cmd/ck-client/ck-client.go | 3 +-- cmd/ck-server/ck-server.go | 3 +-- cmd/ck-server/keygen.go | 3 ++- internal/client/chrome.go | 4 ++-- internal/client/firefox.go | 4 ++-- internal/multiplex/obfs.go | 4 ++-- internal/multiplex/session.go | 6 +++--- internal/server/TLSAux.go | 7 +++---- internal/server/websocket.go | 3 +-- internal/util/util.go | 22 ++++++++++++++++++++++ 10 files changed, 39 insertions(+), 20 deletions(-) diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index 521d551..39c733b 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -3,7 +3,6 @@ package main import ( - "crypto/rand" "encoding/base64" "encoding/binary" "flag" @@ -29,7 +28,7 @@ func makeSession(sta *client.State, isAdmin bool) *mux.Session { // sessionID is usergenerated. There shouldn't be a security concern because the scope of // sessionID is limited to its UID. quad := make([]byte, 4) - rand.Read(quad) + util.CryptoRandRead(quad) atomic.StoreUint32(&sta.SessionID, binary.BigEndian.Uint32(quad)) } diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index 8014629..967a904 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "crypto/rand" "encoding/base64" "flag" "fmt" @@ -71,7 +70,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) { } sessionKey := make([]byte, 32) - rand.Read(sessionKey) + util.CryptoRandRead(sessionKey) obfuscator, err := mux.GenerateObfs(ci.EncryptionMethod, sessionKey, ci.Transport.HasRecordLayer()) if err != nil { log.Error(err) diff --git a/cmd/ck-server/keygen.go b/cmd/ck-server/keygen.go index 3e84049..dd184ca 100644 --- a/cmd/ck-server/keygen.go +++ b/cmd/ck-server/keygen.go @@ -3,11 +3,12 @@ package main import ( "crypto/rand" "github.com/cbeuw/Cloak/internal/ecdh" + "github.com/cbeuw/Cloak/internal/util" ) func generateUID() string { UID := make([]byte, 16) - rand.Read(UID) + util.CryptoRandRead(UID) return b64(UID) } diff --git a/internal/client/chrome.go b/internal/client/chrome.go index c0a78d8..3e674a8 100644 --- a/internal/client/chrome.go +++ b/internal/client/chrome.go @@ -3,9 +3,9 @@ package client import ( - "crypto/rand" "encoding/binary" "encoding/hex" + "github.com/cbeuw/Cloak/internal/util" ) type Chrome struct{} @@ -14,7 +14,7 @@ func makeGREASE() []byte { // see https://tools.ietf.org/html/draft-davidben-tls-grease-01 // This is exclusive to Chrome. var one [1]byte - rand.Read(one[:]) + util.CryptoRandRead(one[:]) sixteenth := one[0] % 16 monoGREASE := byte(sixteenth*16 + 0xA) doubleGREASE := []byte{monoGREASE, monoGREASE} diff --git a/internal/client/firefox.go b/internal/client/firefox.go index fe26468..3e53db8 100644 --- a/internal/client/firefox.go +++ b/internal/client/firefox.go @@ -3,9 +3,9 @@ package client import ( - "crypto/rand" "encoding/binary" "encoding/hex" + "github.com/cbeuw/Cloak/internal/util" ) type Firefox struct{} @@ -19,7 +19,7 @@ func (f *Firefox) composeExtensions(SNI []byte, keyShare []byte) []byte { copy(ret[6:38], hidden) ret[38], ret[39] = 0x00, 0x17 // group secp256r1 ret[40], ret[41] = 0x00, 0x41 // length 65 - rand.Read(ret[42:107]) + util.CryptoRandRead(ret[42:107]) return ret } // extension length is always 399, and server name length is variable diff --git a/internal/multiplex/obfs.go b/internal/multiplex/obfs.go index c8479a1..78ac335 100644 --- a/internal/multiplex/obfs.go +++ b/internal/multiplex/obfs.go @@ -3,10 +3,10 @@ package multiplex import ( "crypto/aes" "crypto/cipher" - "crypto/rand" "encoding/binary" "errors" "fmt" + "github.com/cbeuw/Cloak/internal/util" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/salsa20" ) @@ -64,7 +64,7 @@ func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD, hasRecordLayer bool) if payloadCipher == nil { copy(encryptedPayloadWithExtra, f.Payload) if extraLen != 0 { - rand.Read(encryptedPayloadWithExtra[len(encryptedPayloadWithExtra)-int(extraLen):]) + util.CryptoRandRead(encryptedPayloadWithExtra[len(encryptedPayloadWithExtra)-int(extraLen):]) } } else { ciphertext := payloadCipher.Seal(nil, header[:12], f.Payload, nil) diff --git a/internal/multiplex/session.go b/internal/multiplex/session.go index 0c8b9a7..6dfa8a8 100644 --- a/internal/multiplex/session.go +++ b/internal/multiplex/session.go @@ -1,9 +1,9 @@ package multiplex import ( - "crypto/rand" "errors" "fmt" + "github.com/cbeuw/Cloak/internal/util" "net" "sync" "sync/atomic" @@ -251,9 +251,9 @@ func (sesh *Session) passiveClose() error { func genRandomPadding() []byte { lenB := make([]byte, 1) - rand.Read(lenB) + util.CryptoRandRead(lenB) pad := make([]byte, lenB[0]) - rand.Read(pad) + util.CryptoRandRead(pad) return pad } diff --git a/internal/server/TLSAux.go b/internal/server/TLSAux.go index 779584a..b38c85e 100644 --- a/internal/server/TLSAux.go +++ b/internal/server/TLSAux.go @@ -2,7 +2,6 @@ package server import ( "bytes" - "crypto/rand" "encoding/binary" "encoding/hex" "errors" @@ -164,7 +163,7 @@ func parseClientHello(data []byte) (ret *ClientHello, err error) { func composeServerHello(sessionId []byte, sharedSecret []byte, sessionKey []byte) ([]byte, error) { nonce := make([]byte, 12) - rand.Read(nonce) + util.CryptoRandRead(nonce) encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret, sessionKey) // 32 + 16 = 48 bytes if err != nil { @@ -185,7 +184,7 @@ func composeServerHello(sessionId []byte, sharedSecret []byte, sessionKey []byte keyShare, _ := hex.DecodeString("00330024001d0020") keyExchange := make([]byte, 32) copy(keyExchange, encryptedKey[20:48]) - rand.Read(keyExchange[28:32]) + util.CryptoRandRead(keyExchange[28:32]) serverHello[9] = append(keyShare, keyExchange...) serverHello[10], _ = hex.DecodeString("002b00020304") @@ -207,7 +206,7 @@ func composeReply(ch *ClientHello, sharedSecret []byte, sessionKey []byte) ([]by shBytes := addRecordLayer(sh, []byte{0x16}, TLS12) ccsBytes := addRecordLayer([]byte{0x01}, []byte{0x14}, TLS12) cert := make([]byte, 68) // TODO: add some different lengths maybe? - rand.Read(cert) + util.CryptoRandRead(cert) encryptedCertBytes := addRecordLayer(cert, []byte{0x17}, TLS12) ret := append(shBytes, ccsBytes...) ret = append(ret, encryptedCertBytes...) diff --git a/internal/server/websocket.go b/internal/server/websocket.go index 175c647..5500e65 100644 --- a/internal/server/websocket.go +++ b/internal/server/websocket.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "crypto" - "crypto/rand" "encoding/base64" "errors" "fmt" @@ -45,7 +44,7 @@ func (WebSocket) handshake(reqPacket []byte, privateKey crypto.PrivateKey, origi <-handler.finished preparedConn = handler.conn nonce := make([]byte, 12) - rand.Read(nonce) + util.CryptoRandRead(nonce) // reply: [12 bytes nonce][32 bytes encrypted session key][16 bytes authentication tag] encryptedKey, err := util.AESGCMEncrypt(nonce, ai.sharedSecret[:], sessionKey) // 32 + 16 = 48 bytes diff --git a/internal/util/util.go b/internal/util/util.go index add07a1..f45ab8d 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -3,12 +3,15 @@ package util import ( "crypto/aes" "crypto/cipher" + "crypto/rand" "encoding/binary" "errors" "io" "net" "strconv" "time" + + log "github.com/sirupsen/logrus" ) func AESGCMEncrypt(nonce []byte, key []byte, plaintext []byte) ([]byte, error) { @@ -39,6 +42,25 @@ func AESGCMDecrypt(nonce []byte, key []byte, ciphertext []byte) ([]byte, error) return plain, nil } +func CryptoRandRead(buf []byte) { + _, err := rand.Read(buf) + if err == nil { + return + } + waitDur := [10]time.Duration{5 * time.Millisecond, 10 * time.Millisecond, 30 * time.Millisecond, 50 * time.Millisecond, + 100 * time.Millisecond, 300 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second, + 3 * time.Second, 5 * time.Second} + for i := 0; i < 10; i++ { + log.Errorf("Failed to get cryptographic random bytes: %v. Retrying...", err) + _, err = rand.Read(buf) + if err == nil { + return + } + time.Sleep(time.Millisecond * waitDur[i]) + } + log.Fatal("Cannot get cryptographic random bytes after 10 retries") +} + // ReadTLS reads TLS data according to its record layer func ReadTLS(conn net.Conn, buffer []byte) (n int, err error) { // TCP is a stream. Multiple TLS messages can arrive at the same time,