From b3f0f51775ae2e19c62c70e15f77ef991ad4bb49 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sat, 24 May 2014 04:47:10 +0000 Subject: Move utils.go to csrand/csrand.go, and clean up the interface. All of the obfs4 code except unit tests now uses the csrand wrapper routines. --- csrand/csrand.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ handshake_ntor.go | 8 ++--- ntor/ntor.go | 5 +-- obfs4.go | 6 ++-- replay_filter.go | 5 +-- utils.go | 69 --------------------------------------- weighted_dist.go | 7 ++-- 7 files changed, 114 insertions(+), 83 deletions(-) create mode 100644 csrand/csrand.go delete mode 100644 utils.go diff --git a/csrand/csrand.go b/csrand/csrand.go new file mode 100644 index 0000000..1700b02 --- /dev/null +++ b/csrand/csrand.go @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014, Yawning Angel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +// Package csrand implements the math/rand interface over crypto/rand, along +// with some utility functions for common random number/byte related tasks. +// +// Not all of the convinience routines are replicated, only those that are +// useful for obfs4. The CsRand variable provides access to the full math/rand +// API. +package csrand + +import ( + cryptRand "crypto/rand" + "fmt" + "math/big" + "math/rand" +) + +var ( + csRandSourceInstance csRandSource + + // CsRand is a math/rand instance backed by crypto/rand CSPRNG. + CsRand = rand.New(csRandSourceInstance) +) + +type csRandSource struct { + // This does not keep any state as it is backed by crypto/rand. +} + +func (r csRandSource) Int63() int64 { + ret, err := cryptRand.Int(cryptRand.Reader, big.NewInt(int64((1<<63)-1))) + if err != nil { + panic(err) + } + + return ret.Int64() +} + +func (r csRandSource) Seed(seed int64) { + // No-op. +} + +// Float64 returns, as a float 64, a pesudo random number in [0.0,1.0). +func Float64() float64 { + return CsRand.Float64() +} + +// IntRange returns a uniformly distributed int [min, max]. +func IntRange(min, max int) int { + if max < min { + panic(fmt.Sprintf("IntRange: min > max (%d, %d)", min, max)) + } + + r := (max + 1) - min + ret := CsRand.Intn(r) + return ret + min +} + +// Bytes fills the slice with random data. +func Bytes(buf []byte) error { + n, err := cryptRand.Read(buf) + if err != nil { + // Yes, the go idiom is to check the length, but we panic() when it + // does not match because the system is screwed at that point. + return err + } else if n != len(buf) { + panic(fmt.Sprintf("Bytes: truncated rand.Read (%d, %d)", n, len(buf))) + } + + return nil +} + +/* vim :set ts=4 sw=4 sts=4 noet : */ diff --git a/handshake_ntor.go b/handshake_ntor.go index 8baa382..fc107c2 100644 --- a/handshake_ntor.go +++ b/handshake_ntor.go @@ -30,7 +30,6 @@ package obfs4 import ( "bytes" "crypto/hmac" - "crypto/rand" "crypto/sha256" "encoding/hex" "errors" @@ -39,6 +38,7 @@ import ( "strconv" "time" + "github.com/yawning/obfs4/csrand" "github.com/yawning/obfs4/framing" "github.com/yawning/obfs4/ntor" ) @@ -131,7 +131,7 @@ func newClientHandshake(nodeID *ntor.NodeID, serverIdentity *ntor.PublicKey) (*c } hs.nodeID = nodeID hs.serverIdentity = serverIdentity - hs.padLen = randRange(clientMinPadLength, clientMaxPadLength) + hs.padLen = csrand.IntRange(clientMinPadLength, clientMaxPadLength) hs.mac = hmac.New(sha256.New, append(hs.serverIdentity.Bytes()[:], hs.nodeID.Bytes()[:]...)) return hs, nil @@ -245,7 +245,7 @@ func newServerHandshake(nodeID *ntor.NodeID, serverIdentity *ntor.Keypair) *serv hs := new(serverHandshake) hs.nodeID = nodeID hs.serverIdentity = serverIdentity - hs.padLen = randRange(serverMinPadLength, serverMaxPadLength) + hs.padLen = csrand.IntRange(serverMinPadLength, serverMaxPadLength) hs.mac = hmac.New(sha256.New, append(hs.serverIdentity.Public().Bytes()[:], hs.nodeID.Bytes()[:]...)) return hs @@ -428,7 +428,7 @@ func findMarkMac(mark, buf []byte, startPos, maxPos int, fromTail bool) (pos int func makePad(padLen int) ([]byte, error) { pad := make([]byte, padLen) - _, err := rand.Read(pad) + err := csrand.Bytes(pad) if err != nil { return nil, err } diff --git a/ntor/ntor.go b/ntor/ntor.go index eecf038..4483a6b 100644 --- a/ntor/ntor.go +++ b/ntor/ntor.go @@ -39,7 +39,6 @@ package ntor import ( "bytes" "crypto/hmac" - "crypto/rand" "crypto/sha256" "crypto/subtle" "encoding/base64" @@ -50,6 +49,8 @@ import ( "code.google.com/p/go.crypto/hkdf" "github.com/agl/ed25519/extra25519" + + "github.com/yawning/obfs4/csrand" ) const ( @@ -266,7 +267,7 @@ func NewKeypair(elligator bool) (*Keypair, error) { // Generate a Curve25519 private key. Like everyone who does this, // run the CSPRNG output through SHA256 for extra tinfoil hattery. priv := keypair.private.Bytes()[:] - _, err := rand.Read(priv) + err := csrand.Bytes(priv) if err != nil { return nil, err } diff --git a/obfs4.go b/obfs4.go index 3bbccb3..6978a97 100644 --- a/obfs4.go +++ b/obfs4.go @@ -51,7 +51,7 @@ const ( maxCloseDelayBytes = maxHandshakeLength maxCloseDelay = 60 - maxIatDelay = 100 + maxIatDelay = 100 ) type connState int @@ -472,7 +472,7 @@ func (c *Obfs4Conn) Write(b []byte) (n int, err error) { if err != nil { return 0, err } - time.Sleep(iatDelta * time.Microsecond) + time.Sleep(iatDelta * time.Microsecond) } } else { _, err = c.conn.Write(frameBuf.Bytes()) @@ -584,7 +584,7 @@ type Obfs4Listener struct { keyPair *ntor.Keypair nodeID *ntor.NodeID - seed *DrbgSeed + seed *DrbgSeed iatObfuscation bool closeDelayBytes int diff --git a/replay_filter.go b/replay_filter.go index 5e98b89..9556ef4 100644 --- a/replay_filter.go +++ b/replay_filter.go @@ -29,11 +29,12 @@ package obfs4 import ( "container/list" - "crypto/rand" "encoding/binary" "sync" "github.com/dchest/siphash" + + "github.com/yawning/obfs4/csrand" ) // maxFilterSize is the maximum capacity of the replay filter. The busiest @@ -63,7 +64,7 @@ type filterEntry struct { func newReplayFilter() (filter *replayFilter, err error) { // Initialize the SipHash-2-4 instance with a random key. var key [16]byte - _, err = rand.Read(key[:]) + err = csrand.Bytes(key[:]) if err != nil { return } diff --git a/utils.go b/utils.go deleted file mode 100644 index 51d99d8..0000000 --- a/utils.go +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2014, Yawning Angel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package obfs4 - -import ( - csrand "crypto/rand" - "fmt" - "math/big" - "math/rand" -) - -var ( - csRandSourceInstance csRandSource - csRandInstance = rand.New(csRandSourceInstance) -) - -type csRandSource struct { - // This does not keep any state as it is backed by crypto/rand. -} - -func (r csRandSource) Int63() int64 { - ret, err := csrand.Int(csrand.Reader, big.NewInt(int64((1<<63)-1))) - if err != nil { - panic(err) - } - - return ret.Int64() -} - -func (r csRandSource) Seed(seed int64) { - // No-op. -} - -func randRange(min, max int) int { - if max < min { - panic(fmt.Sprintf("randRange: min > max (%d, %d)", min, max)) - } - - r := (max + 1) - min - ret := csRandInstance.Intn(r) - return ret + min -} - -/* vim :set ts=4 sw=4 sts=4 noet : */ diff --git a/weighted_dist.go b/weighted_dist.go index af0fa7d..04b0d2d 100644 --- a/weighted_dist.go +++ b/weighted_dist.go @@ -28,7 +28,6 @@ package obfs4 import ( - csrand "crypto/rand" "encoding/base64" "encoding/binary" "fmt" @@ -36,6 +35,8 @@ import ( "math/rand" "github.com/dchest/siphash" + + "github.com/yawning/obfs4/csrand" ) // DrbgSeedLength is the length of the hashDrbg seed. @@ -58,7 +59,7 @@ func (seed *DrbgSeed) Base64() string { // NewDrbgSeed returns a DrbgSeed initialized with the runtime CSPRNG. func NewDrbgSeed() (seed *DrbgSeed, err error) { seed = new(DrbgSeed) - _, err = csrand.Read(seed.Bytes()[:]) + err = csrand.Bytes(seed.Bytes()[:]) if err != nil { return nil, err } @@ -160,7 +161,7 @@ func newWDist(seed *DrbgSeed, min, max int) (w *wDist) { func (w *wDist) sample() int { retIdx := 0 totalProb := 0.0 - prob := csRandInstance.Float64() + prob := csrand.Float64() for i, bucketProb := range w.buckets { totalProb += bucketProb if prob <= totalProb { -- cgit v1.2.3