summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorBrandon Wiley <brandon@blanu.net>2016-11-15 15:09:06 -0600
committerBrandon Wiley <brandon@blanu.net>2016-11-15 15:09:06 -0600
commit730f4bd72f111da13dd7d68fb350aea98f4c19bf (patch)
tree998c36bfcc8fff286654823ca5d60ccd580d4332 /common
parent8a7de7e1e252a73d786bafab042eedde8c025ad6 (diff)
Fixed import paths to use shapeshifter-dispatcher/shapeshifter-transports instead of obfs4
Diffstat (limited to 'common')
-rw-r--r--common/csrand/csrand.go101
-rw-r--r--common/drbg/hash_drbg.go149
-rw-r--r--common/ntor/ntor.go433
-rw-r--r--common/ntor/ntor_test.go180
-rw-r--r--common/probdist/weighted_dist.go245
-rw-r--r--common/probdist/weighted_dist_test.go80
-rw-r--r--common/replayfilter/replay_filter.go147
-rw-r--r--common/replayfilter/replay_filter_test.go95
-rw-r--r--common/termmon/termmon.go4
-rw-r--r--common/uniformdh/uniformdh.go183
-rw-r--r--common/uniformdh/uniformdh_test.go220
11 files changed, 2 insertions, 1835 deletions
diff --git a/common/csrand/csrand.go b/common/csrand/csrand.go
deleted file mode 100644
index 45849d3..0000000
--- a/common/csrand/csrand.go
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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
-// immediately useful. The Rand variable provides access to the full math/rand
-// API.
-package csrand
-
-import (
- cryptRand "crypto/rand"
- "encoding/binary"
- "fmt"
- "io"
- "math/rand"
-)
-
-var (
- csRandSourceInstance csRandSource
-
- // Rand is a math/rand instance backed by crypto/rand CSPRNG.
- Rand = rand.New(csRandSourceInstance)
-)
-
-type csRandSource struct {
- // This does not keep any state as it is backed by crypto/rand.
-}
-
-func (r csRandSource) Int63() int64 {
- var src [8]byte
- if err := Bytes(src[:]); err != nil {
- panic(err)
- }
- val := binary.BigEndian.Uint64(src[:])
- val &= (1<<63 - 1)
-
- return int64(val)
-}
-
-func (r csRandSource) Seed(seed int64) {
- // No-op.
-}
-
-// Intn returns, as a int, a pseudo random number in [0, n).
-func Intn(n int) int {
- return Rand.Intn(n)
-}
-
-// Float64 returns, as a float64, a pesudo random number in [0.0,1.0).
-func Float64() float64 {
- return Rand.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 := Rand.Intn(r)
- return ret + min
-}
-
-// Bytes fills the slice with random data.
-func Bytes(buf []byte) error {
- if _, err := io.ReadFull(cryptRand.Reader, buf); err != nil {
- return err
- }
-
- return nil
-}
-
-// Reader is a alias of rand.Reader.
-var Reader = cryptRand.Reader
diff --git a/common/drbg/hash_drbg.go b/common/drbg/hash_drbg.go
deleted file mode 100644
index cc4e8b9..0000000
--- a/common/drbg/hash_drbg.go
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 drbg implements a minimalistic DRBG based off SipHash-2-4 in OFB
-// mode.
-package drbg
-
-import (
- "encoding/binary"
- "encoding/hex"
- "fmt"
- "hash"
-
- "github.com/dchest/siphash"
-
- "github.com/OperatorFoundation/obfs4/common/csrand"
-)
-
-// Size is the length of the HashDrbg output.
-const Size = siphash.Size
-
-// SeedLength is the length of the HashDrbg seed.
-const SeedLength = 16 + Size
-
-// Seed is the initial state for a HashDrbg. It consists of a SipHash-2-4
-// key, and 8 bytes of initial data.
-type Seed [SeedLength]byte
-
-// Bytes returns a pointer to the raw HashDrbg seed.
-func (seed *Seed) Bytes() *[SeedLength]byte {
- return (*[SeedLength]byte)(seed)
-}
-
-// Hex returns the hexdecimal representation of the seed.
-func (seed *Seed) Hex() string {
- return hex.EncodeToString(seed.Bytes()[:])
-}
-
-// NewSeed returns a Seed initialized with the runtime CSPRNG.
-func NewSeed() (seed *Seed, err error) {
- seed = new(Seed)
- if err = csrand.Bytes(seed.Bytes()[:]); err != nil {
- return nil, err
- }
-
- return
-}
-
-// SeedFromBytes creates a Seed from the raw bytes, truncating to SeedLength as
-// appropriate.
-func SeedFromBytes(src []byte) (seed *Seed, err error) {
- if len(src) < SeedLength {
- return nil, InvalidSeedLengthError(len(src))
- }
-
- seed = new(Seed)
- copy(seed.Bytes()[:], src)
-
- return
-}
-
-// SeedFromHex creates a Seed from the hexdecimal representation, truncating to
-// SeedLength as appropriate.
-func SeedFromHex(encoded string) (seed *Seed, err error) {
- var raw []byte
- if raw, err = hex.DecodeString(encoded); err != nil {
- return nil, err
- }
-
- return SeedFromBytes(raw)
-}
-
-// InvalidSeedLengthError is the error returned when the seed provided to the
-// DRBG is an invalid length.
-type InvalidSeedLengthError int
-
-func (e InvalidSeedLengthError) Error() string {
- return fmt.Sprintf("invalid seed length: %d", int(e))
-}
-
-// HashDrbg is a CSDRBG based off of SipHash-2-4 in OFB mode.
-type HashDrbg struct {
- sip hash.Hash64
- ofb [Size]byte
-}
-
-// NewHashDrbg makes a HashDrbg instance based off an optional seed. The seed
-// is truncated to SeedLength.
-func NewHashDrbg(seed *Seed) (*HashDrbg, error) {
- drbg := new(HashDrbg)
- if seed == nil {
- var err error
- if seed, err = NewSeed(); err != nil {
- return nil, err
- }
- }
- drbg.sip = siphash.New(seed.Bytes()[:16])
- copy(drbg.ofb[:], seed.Bytes()[16:])
-
- return drbg, nil
-}
-
-// Int63 returns a uniformly distributed random integer [0, 1 << 63).
-func (drbg *HashDrbg) Int63() int64 {
- block := drbg.NextBlock()
- ret := binary.BigEndian.Uint64(block)
- ret &= (1<<63 - 1)
-
- return int64(ret)
-}
-
-// Seed does nothing, call NewHashDrbg if you want to reseed.
-func (drbg *HashDrbg) Seed(seed int64) {
- // No-op.
-}
-
-// NextBlock returns the next 8 byte DRBG block.
-func (drbg *HashDrbg) NextBlock() []byte {
- drbg.sip.Write(drbg.ofb[:])
- copy(drbg.ofb[:], drbg.sip.Sum(nil))
-
- ret := make([]byte, Size)
- copy(ret, drbg.ofb[:])
- return ret
-}
diff --git a/common/ntor/ntor.go b/common/ntor/ntor.go
deleted file mode 100644
index 0994782..0000000
--- a/common/ntor/ntor.go
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 ntor implements the Tor Project's ntor handshake as defined in
-// proposal 216 "Improved circuit-creation key exchange". It also supports
-// using Elligator to transform the Curve25519 public keys sent over the wire
-// to a form that is indistinguishable from random strings.
-//
-// Before using this package, it is strongly recommended that the specification
-// is read and understood.
-package ntor
-
-import (
- "bytes"
- "crypto/hmac"
- "crypto/sha256"
- "crypto/subtle"
- "encoding/hex"
- "fmt"
- "io"
-
- "golang.org/x/crypto/curve25519"
- "golang.org/x/crypto/hkdf"
-
- "github.com/agl/ed25519/extra25519"
-
- "github.com/OperatorFoundation/obfs4/common/csrand"
-)
-
-const (
- // PublicKeyLength is the length of a Curve25519 public key.
- PublicKeyLength = 32
-
- // RepresentativeLength is the length of an Elligator representative.
- RepresentativeLength = 32
-
- // PrivateKeyLength is the length of a Curve25519 private key.
- PrivateKeyLength = 32
-
- // SharedSecretLength is the length of a Curve25519 shared secret.
- SharedSecretLength = 32
-
- // NodeIDLength is the length of a ntor node identifier.
- NodeIDLength = 20
-
- // KeySeedLength is the length of the derived KEY_SEED.
- KeySeedLength = sha256.Size
-
- // AuthLength is the lenght of the derived AUTH.
- AuthLength = sha256.Size
-)
-
-var protoID = []byte("ntor-curve25519-sha256-1")
-var tMac = append(protoID, []byte(":mac")...)
-var tKey = append(protoID, []byte(":key_extract")...)
-var tVerify = append(protoID, []byte(":key_verify")...)
-var mExpand = append(protoID, []byte(":key_expand")...)
-
-// PublicKeyLengthError is the error returned when the public key being
-// imported is an invalid length.
-type PublicKeyLengthError int
-
-func (e PublicKeyLengthError) Error() string {
- return fmt.Sprintf("ntor: Invalid Curve25519 public key length: %d",
- int(e))
-}
-
-// PrivateKeyLengthError is the error returned when the private key being
-// imported is an invalid length.
-type PrivateKeyLengthError int
-
-func (e PrivateKeyLengthError) Error() string {
- return fmt.Sprintf("ntor: Invalid Curve25519 private key length: %d",
- int(e))
-}
-
-// NodeIDLengthError is the error returned when the node ID being imported is
-// an invalid length.
-type NodeIDLengthError int
-
-func (e NodeIDLengthError) Error() string {
- return fmt.Sprintf("ntor: Invalid NodeID length: %d", int(e))
-}
-
-// KeySeed is the key material that results from a handshake (KEY_SEED).
-type KeySeed [KeySeedLength]byte
-
-// Bytes returns a pointer to the raw key material.
-func (key_seed *KeySeed) Bytes() *[KeySeedLength]byte {
- return (*[KeySeedLength]byte)(key_seed)
-}
-
-// Auth is the verifier that results from a handshake (AUTH).
-type Auth [AuthLength]byte
-
-// Bytes returns a pointer to the raw auth.
-func (auth *Auth) Bytes() *[AuthLength]byte {
- return (*[AuthLength]byte)(auth)
-}
-
-// NodeID is a ntor node identifier.
-type NodeID [NodeIDLength]byte
-
-// NewNodeID creates a NodeID from the raw bytes.
-func NewNodeID(raw []byte) (*NodeID, error) {
- if len(raw) != NodeIDLength {
- return nil, NodeIDLengthError(len(raw))
- }
-
- nodeID := new(NodeID)
- copy(nodeID[:], raw)
-
- return nodeID, nil
-}
-
-// NodeIDFromHex creates a new NodeID from the hexdecimal representation.
-func NodeIDFromHex(encoded string) (*NodeID, error) {
- raw, err := hex.DecodeString(encoded)
- if err != nil {
- return nil, err
- }
-
- return NewNodeID(raw)
-}
-
-// Bytes returns a pointer to the raw NodeID.
-func (id *NodeID) Bytes() *[NodeIDLength]byte {
- return (*[NodeIDLength]byte)(id)
-}
-
-// Hex returns the hexdecimal representation of the NodeID.
-func (id *NodeID) Hex() string {
- return hex.EncodeToString(id[:])
-}
-
-// PublicKey is a Curve25519 public key in little-endian byte order.
-type PublicKey [PublicKeyLength]byte
-
-// Bytes returns a pointer to the raw Curve25519 public key.
-func (public *PublicKey) Bytes() *[PublicKeyLength]byte {
- return (*[PublicKeyLength]byte)(public)
-}
-
-// Hex returns the hexdecimal representation of the Curve25519 public key.
-func (public *PublicKey) Hex() string {
- return hex.EncodeToString(public.Bytes()[:])
-}
-
-// NewPublicKey creates a PublicKey from the raw bytes.
-func NewPublicKey(raw []byte) (*PublicKey, error) {
- if len(raw) != PublicKeyLength {
- return nil, PublicKeyLengthError(len(raw))
- }
-
- pubKey := new(PublicKey)
- copy(pubKey[:], raw)
-
- return pubKey, nil
-}
-
-// PublicKeyFromHex returns a PublicKey from the hexdecimal representation.
-func PublicKeyFromHex(encoded string) (*PublicKey, error) {
- raw, err := hex.DecodeString(encoded)
- if err != nil {
- return nil, err
- }
-
- return NewPublicKey(raw)
-}
-
-// Representative is an Elligator representative of a Curve25519 public key
-// in little-endian byte order.
-type Representative [RepresentativeLength]byte
-
-// Bytes returns a pointer to the raw Elligator representative.
-func (repr *Representative) Bytes() *[RepresentativeLength]byte {
- return (*[RepresentativeLength]byte)(repr)
-}
-
-// ToPublic converts a Elligator representative to a Curve25519 public key.
-func (repr *Representative) ToPublic() *PublicKey {
- pub := new(PublicKey)
-
- extra25519.RepresentativeToPublicKey(pub.Bytes(), repr.Bytes())
- return pub
-}
-
-// PrivateKey is a Curve25519 private key in little-endian byte order.
-type PrivateKey [PrivateKeyLength]byte
-
-// Bytes returns a pointer to the raw Curve25519 private key.
-func (private *PrivateKey) Bytes() *[PrivateKeyLength]byte {
- return (*[PrivateKeyLength]byte)(private)
-}
-
-// Hex returns the hexdecimal representation of the Curve25519 private key.
-func (private *PrivateKey) Hex() string {
- return hex.EncodeToString(private.Bytes()[:])
-}
-
-// Keypair is a Curve25519 keypair with an optional Elligator representative.
-// As only certain Curve25519 keys can be obfuscated with Elligator, the
-// representative must be generated along with the keypair.
-type Keypair struct {
- public *PublicKey
- private *PrivateKey
- representative *Representative
-}
-
-// Public returns the Curve25519 public key belonging to the Keypair.
-func (keypair *Keypair) Public() *PublicKey {
- return keypair.public
-}
-
-// Private returns the Curve25519 private key belonging to the Keypair.
-func (keypair *Keypair) Private() *PrivateKey {
- return keypair.private
-}
-
-// Representative returns the Elligator representative of the public key
-// belonging to the Keypair.
-func (keypair *Keypair) Representative() *Representative {
- return keypair.representative
-}
-
-// HasElligator returns true if the Keypair has an Elligator representative.
-func (keypair *Keypair) HasElligator() bool {
- return nil != keypair.representative
-}
-
-// NewKeypair generates a new Curve25519 keypair, and optionally also generates
-// an Elligator representative of the public key.
-func NewKeypair(elligator bool) (*Keypair, error) {
- keypair := new(Keypair)
- keypair.private = new(PrivateKey)
- keypair.public = new(PublicKey)
- if elligator {
- keypair.representative = new(Representative)
- }
-
- for {
- // Generate a Curve25519 private key. Like everyone who does this,
- // run the CSPRNG output through SHA256 for extra tinfoil hattery.
- priv := keypair.private.Bytes()[:]
- if err := csrand.Bytes(priv); err != nil {
- return nil, err
- }
- digest := sha256.Sum256(priv)
- digest[0] &= 248
- digest[31] &= 127
- digest[31] |= 64
- copy(priv, digest[:])
-
- if elligator {
- // Apply the Elligator transform. This fails ~50% of the time.
- if !extra25519.ScalarBaseMult(keypair.public.Bytes(),
- keypair.representative.Bytes(),
- keypair.private.Bytes()) {
- continue
- }
- } else {
- // Generate the corresponding Curve25519 public key.
- curve25519.ScalarBaseMult(keypair.public.Bytes(),
- keypair.private.Bytes())
- }
-
- return keypair, nil
- }
-}
-
-// KeypairFromHex returns a Keypair from the hexdecimal representation of the
-// private key.
-func KeypairFromHex(encoded string) (*Keypair, error) {
- raw, err := hex.DecodeString(encoded)
- if err != nil {
- return nil, err
- }
-
- if len(raw) != PrivateKeyLength {
- return nil, PrivateKeyLengthError(len(raw))
- }
-
- keypair := new(Keypair)
- keypair.private = new(PrivateKey)
- keypair.public = new(PublicKey)
-
- copy(keypair.private[:], raw)
- curve25519.ScalarBaseMult(keypair.public.Bytes(),
- keypair.private.Bytes())
-
- return keypair, nil
-}
-
-// ServerHandshake does the server side of a ntor handshake and returns status,
-// KEY_SEED, and AUTH. If status is not true, the handshake MUST be aborted.
-func ServerHandshake(clientPublic *PublicKey, serverKeypair *Keypair, idKeypair *Keypair, id *NodeID) (ok bool, keySeed *KeySeed, auth *Auth) {
- var notOk int
- var secretInput bytes.Buffer
-
- // Server side uses EXP(X,y) | EXP(X,b)
- var exp [SharedSecretLength]byte
- curve25519.ScalarMult(&exp, serverKeypair.private.Bytes(),
- clientPublic.Bytes())
- notOk |= constantTimeIsZero(exp[:])
- secretInput.Write(exp[:])
-
- curve25519.ScalarMult(&exp, idKeypair.private.Bytes(),
- clientPublic.Bytes())
- notOk |= constantTimeIsZero(exp[:])
- secretInput.Write(exp[:])
-
- keySeed, auth = ntorCommon(secretInput, id, idKeypair.public,
- clientPublic, serverKeypair.public)
- return notOk == 0, keySeed, auth
-}
-
-// ClientHandshake does the client side of a ntor handshake and returnes
-// status, KEY_SEED, and AUTH. If status is not true or AUTH does not match
-// the value recieved from the server, the handshake MUST be aborted.
-func ClientHandshake(clientKeypair *Keypair, serverPublic *PublicKey, idPublic *PublicKey, id *NodeID) (ok bool, keySeed *KeySeed, auth *Auth) {
- var notOk int
- var secretInput bytes.Buffer
-
- // Client side uses EXP(Y,x) | EXP(B,x)
- var exp [SharedSecretLength]byte
- curve25519.ScalarMult(&exp, clientKeypair.private.Bytes(),
- serverPublic.Bytes())
- notOk |= constantTimeIsZero(exp[:])
- secretInput.Write(exp[:])
-
- curve25519.ScalarMult(&exp, clientKeypair.private.Bytes(),
- idPublic.Bytes())
- notOk |= constantTimeIsZero(exp[:])
- secretInput.Write(exp[:])
-
- keySeed, auth = ntorCommon(secretInput, id, idPublic,
- clientKeypair.public, serverPublic)
- return notOk == 0, keySeed, auth
-}
-
-// CompareAuth does a constant time compare of a Auth and a byte slice
-// (presumably received over a network).
-func CompareAuth(auth1 *Auth, auth2 []byte) bool {
- auth1Bytes := auth1.Bytes()
- return hmac.Equal(auth1Bytes[:], auth2)
-}
-
-func ntorCommon(secretInput bytes.Buffer, id *NodeID, b *PublicKey, x *PublicKey, y *PublicKey) (*KeySeed, *Auth) {
- keySeed := new(KeySeed)
- auth := new(Auth)
-
- // secret_input/auth_input use this common bit, build it once.
- suffix := bytes.NewBuffer(b.Bytes()[:])
- suffix.Write(b.Bytes()[:])
- suffix.Write(x.Bytes()[:])
- suffix.Write(y.Bytes()[:])
- suffix.Write(protoID)
- suffix.Write(id[:])
-
- // At this point secret_input has the 2 exponents, concatenated, append the
- // client/server common suffix.
- secretInput.Write(suffix.Bytes())
-
- // KEY_SEED = H(secret_input, t_key)
- h := hmac.New(sha256.New, tKey)
- h.Write(secretInput.Bytes())
- tmp := h.Sum(nil)
- copy(keySeed[:], tmp)
-
- // verify = H(secret_input, t_verify)
- h = hmac.New(sha256.New, tVerify)
- h.Write(secretInput.Bytes())
- verify := h.Sum(nil)
-
- // auth_input = verify | ID | B | Y | X | PROTOID | "Server"
- authInput := bytes.NewBuffer(verify)
- authInput.Write(suffix.Bytes())
- authInput.Write([]byte("Server"))
- h = hmac.New(sha256.New, tMac)
- h.Write(authInput.Bytes())
- tmp = h.Sum(nil)
- copy(auth[:], tmp)
-
- return keySeed, auth
-}
-
-func constantTimeIsZero(x []byte) int {
- var ret byte
- for _, v := range x {
- ret |= v
- }
-
- return subtle.ConstantTimeByteEq(ret, 0)
-}
-
-// Kdf extracts and expands KEY_SEED via HKDF-SHA256 and returns `okm_len` bytes
-// of key material.
-func Kdf(keySeed []byte, okmLen int) []byte {
- kdf := hkdf.New(sha256.New, keySeed, tKey, mExpand)
- okm := make([]byte, okmLen)
- n, err := io.ReadFull(kdf, okm)
- if err != nil {
- panic(fmt.Sprintf("BUG: Failed HKDF: %s", err.Error()))
- } else if n != len(okm) {
- panic(fmt.Sprintf("BUG: Got truncated HKDF output: %d", n))
- }
-
- return okm
-}
diff --git a/common/ntor/ntor_test.go b/common/ntor/ntor_test.go
deleted file mode 100644
index c92c04e..0000000
--- a/common/ntor/ntor_test.go
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 ntor
-
-import (
- "bytes"
- "testing"
-)
-
-// TestNewKeypair tests Curve25519/Elligator keypair generation.
-func TestNewKeypair(t *testing.T) {
- // Test standard Curve25519 first.
- keypair, err := NewKeypair(false)
- if err != nil {
- t.Fatal("NewKeypair(false) failed:", err)
- }
- if keypair == nil {
- t.Fatal("NewKeypair(false) returned nil")
- }
- if keypair.HasElligator() {
- t.Fatal("NewKeypair(false) has a Elligator representative")
- }
-
- // Test Elligator generation.
- keypair, err = NewKeypair(true)
- if err != nil {
- t.Fatal("NewKeypair(true) failed:", err)
- }
- if keypair == nil {
- t.Fatal("NewKeypair(true) returned nil")
- }
- if !keypair.HasElligator() {
- t.Fatal("NewKeypair(true) mising an Elligator representative")
- }
-}
-
-// Test Client/Server handshake.
-func TestHandshake(t *testing.T) {
- clientKeypair, err := NewKeypair(true)
- if err != nil {
- t.Fatal("Failed to generate client keypair:", err)
- }
- if clientKeypair == nil {
- t.Fatal("Client keypair is nil")
- }
-
- serverKeypair, err := NewKeypair(true)
- if err != nil {
- t.Fatal("Failed to generate server keypair:", err)
- }
- if serverKeypair == nil {
- t.Fatal("Server keypair is nil")
- }
-
- idKeypair, err := NewKeypair(false)
- if err != nil {
- t.Fatal("Failed to generate identity keypair:", err)
- }
- if idKeypair == nil {
- t.Fatal("Identity keypair is nil")
- }
-
- nodeID, err := NewNodeID([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13"))
- if err != nil {
- t.Fatal("Failed to load NodeId:", err)
- }
-
- // ServerHandshake
- clientPublic := clientKeypair.Representative().ToPublic()
- ok, serverSeed, serverAuth := ServerHandshake(clientPublic,
- serverKeypair, idKeypair, nodeID)
- if !ok {
- t.Fatal("ServerHandshake failed")
- }
- if serverSeed == nil {
- t.Fatal("ServerHandshake returned nil KEY_SEED")
- }
- if serverAuth == nil {
- t.Fatal("ServerHandshake returned nil AUTH")
- }
-
- // ClientHandshake
- ok, clientSeed, clientAuth := ClientHandshake(clientKeypair,
- serverKeypair.Public(), idKeypair.Public(), nodeID)
- if !ok {
- t.Fatal("ClientHandshake failed")
- }
- if clientSeed == nil {
- t.Fatal("ClientHandshake returned nil KEY_SEED")
- }
- if clientAuth == nil {
- t.Fatal("ClientHandshake returned nil AUTH")
- }
-
- // WARNING: Use a constant time comparison in actual code.
- if 0 != bytes.Compare(clientSeed.Bytes()[:], serverSeed.Bytes()[:]) {
- t.Fatal("KEY_SEED mismatched between client/server")
- }
- if 0 != bytes.Compare(clientAuth.Bytes()[:], serverAuth.Bytes()[:]) {
- t.Fatal("AUTH mismatched between client/server")
- }
-}
-
-// Benchmark Client/Server handshake. The actual time taken that will be
-// observed on either the Client or Server is half the reported time per
-// operation since the benchmark does both sides.
-func BenchmarkHandshake(b *testing.B) {
- // Generate the "long lasting" identity key and NodeId.
- idKeypair, err := NewKeypair(false)
- if err != nil || idKeypair == nil {
- b.Fatal("Failed to generate identity keypair")
- }
- nodeID, err := NewNodeID([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13"))
- if err != nil {
- b.Fatal("Failed to load NodeId:", err)
- }
- b.ResetTimer()
-
- // Start the actual benchmark.
- for i := 0; i < b.N; i++ {
- // Generate the keypairs.
- serverKeypair, err := NewKeypair(true)
- if err != nil || serverKeypair == nil {
- b.Fatal("Failed to generate server keypair")
- }
-
- clientKeypair, err := NewKeypair(true)
- if err != nil || clientKeypair == nil {
- b.Fatal("Failed to generate client keypair")
- }
-
- // Server handshake.
- clientPublic := clientKeypair.Representative().ToPublic()
- ok, serverSeed, serverAuth := ServerHandshake(clientPublic,
- serverKeypair, idKeypair, nodeID)
- if !ok || serverSeed == nil || serverAuth == nil {
- b.Fatal("ServerHandshake failed")
- }
-
- // Client handshake.
- serverPublic := serverKeypair.Representative().ToPublic()
- ok, clientSeed, clientAuth := ClientHandshake(clientKeypair,
- serverPublic, idKeypair.Public(), nodeID)
- if !ok || clientSeed == nil || clientAuth == nil {
- b.Fatal("ClientHandshake failed")
- }
-
- // Validate the authenticator. Real code would pass the AUTH read off
- // the network as a slice to CompareAuth here.
- if !CompareAuth(clientAuth, serverAuth.Bytes()[:]) ||
- !CompareAuth(serverAuth, clientAuth.Bytes()[:]) {
- b.Fatal("AUTH mismatched between client/server")
- }
- }
-}
diff --git a/common/probdist/weighted_dist.go b/common/probdist/weighted_dist.go
deleted file mode 100644
index 2822ce6..0000000
--- a/common/probdist/weighted_dist.go
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 probdist implements a weighted probability distribution suitable for
-// protocol parameterization. To allow for easy reproduction of a given
-// distribution, the drbg package is used as the random number source.
-package probdist
-
-import (
- "bytes"
- "container/list"
- "fmt"
- "math/rand"
- "sync"
-
- "github.com/OperatorFoundation/obfs4/common/csrand"
- "github.com/OperatorFoundation/obfs4/common/drbg"
-)
-
-const (
- minValues = 1
- maxValues = 100
-)
-
-// WeightedDist is a weighted distribution.
-type WeightedDist struct {
- sync.Mutex
-
- minValue int
- maxValue int
- biased bool
- values []int
- weights []float64
-
- alias []int
- prob []float64
-}
-
-// New creates a weighted distribution of values ranging from min to max
-// based on a HashDrbg initialized with seed. Optionally, bias the weight
-// generation to match the ScrambleSuit non-uniform distribution from
-// obfsproxy.
-func New(seed *drbg.Seed, min, max int, biased bool) (w *WeightedDist) {
- w = &WeightedDist{minValue: min, maxValue: max, biased: biased}
-
- if max <= min {
- panic(fmt.Sprintf("wDist.Reset(): min >= max (%d, %d)", min, max))
- }
-
- w.Reset(seed)
-
- return
-}
-
-// genValues creates a slice containing a random number of random values
-// that when scaled by adding minValue will fall into [min, max].
-func (w *WeightedDist) genValues(rng *rand.Rand) {
- nValues := (w.maxValue + 1) - w.minValue
- values := rng.Perm(nValues)
- if nValues < minValues {
- nValues = minValues
- }
- if nValues > maxValues {
- nValues = maxValues
- }
- nValues = rng.Intn(nValues) + 1
- w.values = values[:nValues]
-}
-
-// genBiasedWeights generates a non-uniform weight list, similar to the
-// ScrambleSuit prob_dist module.
-func (w *WeightedDist) genBiasedWeights(rng *rand.Rand) {
- w.weights = make([]float64, len(w.values))
-
- culmProb := 0.0
- for i := range w.weights {
- p := (1.0 - culmProb) * rng.Float64()
- w.weights[i] = p
- culmProb += p
- }
-}
-
-// genUniformWeights generates a uniform weight list.
-func (w *WeightedDist) genUniformWeights(rng *rand.Rand) {
- w.weights = make([]float64, len(w.values))
- for i := range w.weights {
- w.weights[i] = rng.Float64()
- }
-}
-
-// genTables calculates the alias and prob tables used for Vose's Alias method.
-// Algorithm taken from http://www.keithschwarz.com/darts-dice-coins/
-func (w *WeightedDist) genTables() {
- n := len(w.weights)
- var sum float64
- for _, weight := range w.weights {
- sum += weight
- }
-
- // Create arrays $Alias$ and $Prob$, each of size $n$.
- alias := make([]int, n)
- prob := make([]float64, n)
-
- // Create two worklists, $Small$ and $Large$.
- small := list.New()
- large := list.New()
-
- scaled := make([]float64, n)
- for i, weight := range w.weights {
- // Multiply each probability by $n$.
- p_i := weight * float64(n) / sum
- scaled[i] = p_i
-
- // For each scaled probability $p_i$:
- if scaled[i] < 1.0 {
- // If $p_i < 1$, add $i$ to $Small$.
- small.PushBack(i)
- } else {
- // Otherwise ($p_i \ge 1$), add $i$ to $Large$.
- large.PushBack(i)
- }
- }
-
- // While $Small$ and $Large$ are not empty: ($Large$ might be emptied first)
- for small.Len() > 0 && large.Len() > 0 {
- // Remove the first element from $Small$; call it $l$.
- l := small.Remove(small.Front()).(int)
- // Remove the first element from $Large$; call it $g$.
- g := large.Remove(large.Front()).(int)
-
- // Set $Prob[l] = p_l$.
- prob[l] = scaled[l]
- // Set $Alias[l] = g$.
- alias[l] = g
-
- // Set $p_g := (p_g + p_l) - 1$. (This is a more numerically stable option.)
- scaled[g] = (scaled[g] + scaled[l]) - 1.0
-
- if scaled[g] < 1.0 {
- // If $p_g < 1$, add $g$ to $Small$.
- small.PushBack(g)
- } else {
- // Otherwise ($p_g \ge 1$), add $g$ to $Large$.
- large.PushBack(g)
- }
- }
-
- // While $Large$ is not empty:
- for large.Len() > 0 {
- // Remove the first element from $Large$; call it $g$.
- g := large.Remove(large.Front()).(int)
- // Set $Prob[g] = 1$.
- prob[g] = 1.0
- }
-
- // While $Small$ is not empty: This is only possible due to numerical instability.
- for small.Len() > 0 {
- // Remove the first element from $Small$; call it $l$.
- l := small.Remove(small.Front()).(int)
- // Set $Prob[l] = 1$.
- prob[l] = 1.0
- }
-
- w.prob = prob
- w.alias = alias
-}
-
-// Reset generates a new distribution with the same min/max based on a new
-// seed.
-func (w *WeightedDist) Reset(seed *drbg.Seed) {
- // Initialize the deterministic random number generator.
- drbg, _ := drbg.NewHashDrbg(seed)
- rng := rand.New(drbg)
-
- w.Lock()
- defer w.Unlock()
-
- w.genValues(rng)
- if w.biased {
- w.genBiasedWeights(rng)
- } else {
- w.genUniformWeights(rng)
- }
- w.genTables()
-}
-
-// Sample generates a random value according to the distribution.
-func (w *WeightedDist) Sample() int {
- var idx int
-
- w.Lock()
- defer w.Unlock()
-
- // Generate a fair die roll from an $n$-sided die; call the side $i$.
- i := csrand.Intn(len(w.values))
- // Flip a biased coin that comes up heads with probability $Prob[i]$.
- if csrand.Float64() <= w.prob[i] {
- // If the coin comes up "heads," return $i$.
- idx = i
- } else {
- // Otherwise, return $Alias[i]$.
- idx = w.alias[i]
- }
-
- return w.minValue + w.values[idx]
-}
-
-// String returns a dump of the distribution table.
-func (w *WeightedDist) String() string {
- var buf bytes.Buffer
-
- buf.WriteString("[ ")
- for i, v := range w.values {
- p := w.weights[i]
- if p > 0.01 { // Squelch tiny probabilities.
- buf.WriteString(fmt.Sprintf("%d: %f ", v, p))
- }
- }
- buf.WriteString("]")
- return buf.String()
-}
diff --git a/common/probdist/weighted_dist_test.go b/common/probdist/weighted_dist_test.go
deleted file mode 100644
index ca7247c..0000000
--- a/common/probdist/weighted_dist_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 probdist
-
-import (
- "fmt"
- "testing"
-
- "github.com/OperatorFoundation/obfs4/common/drbg"
-)
-
-const debug = false
-
-func TestWeightedDist(t *testing.T) {
- seed, err := drbg.NewSeed()
- if err != nil {
- t.Fatal("failed to generate a DRBG seed:", err)
- }
-
- const nrTrials = 1000000
-
- hist := make([]int, 1000)
-
- w := New(seed, 0, 999, true)
- if debug {
- // Dump a string representation of the probability table.
- fmt.Println("Table:")
- var sum float64
- for _, weight := range w.weights {
- sum += weight
- }
- for i, weight := range w.weights {
- p := weight / sum
- if p > 0.000001 { // Filter out tiny values.
- fmt.Printf(" [%d]: %f\n", w.minValue+w.values[i], p)
- }
- }
- fmt.Println()
- }
-
- for i := 0; i < nrTrials; i++ {
- value := w.Sample()
- hist[value]++
- }
-
- if debug {
- fmt.Println("Generated:")
- for value, count := range hist {
- if count != 0 {
- p := float64(count) / float64(nrTrials)
- fmt.Printf(" [%d]: %f (%d)\n", value, p, count)
- }
- }
- }
-}
diff --git a/common/replayfilter/replay_filter.go b/common/replayfilter/replay_filter.go
deleted file mode 100644
index 5513361..0000000
--- a/common/replayfilter/replay_filter.go
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 replayfilter implements a generic replay detection filter with a
-// caller specifiable time-to-live. It only detects if a given byte sequence
-// has been seen before based on the SipHash-2-4 digest of the sequence.
-// Collisions are treated as positive matches, though the probability of this
-// happening is negligible.
-package replayfilter
-
-import (
- "container/list"
- "encoding/binary"
- "sync"
- "time"
-
- "github.com/dchest/siphash"
-
- "github.com/OperatorFoundation/obfs4/common/csrand"
-)
-
-// maxFilterSize is the maximum capacity of a replay filter. This value is
-// more as a safeguard to prevent runaway filter growth, and is sized to be
-// serveral orders of magnitude greater than the number of connections a busy
-// bridge sees in one day, so in practice should never be reached.
-const maxFilterSize = 100 * 1024
-
-type entry struct {
- digest uint64
- firstSeen time.Time
- element *list.Element
-}
-
-// ReplayFilter is a simple filter designed only to detect if a given byte
-// sequence has been seen before.
-type ReplayFilter struct {
- sync.Mutex
-
- filter map[uint64]*entry
- fifo *list.List
-
- key [2]uint64
- ttl time.Duration
-}
-
-// New creates a new ReplayFilter instance.
-func New(ttl time.Duration) (filter *ReplayFilter, err error) {
- // Initialize the SipHash-2-4 instance with a random key.
- var key [16]byte
- if err = csrand.Bytes(key[:]); err != nil {
- return
- }
-
- filter = new(ReplayFilter)
- filter.filter = make(map[uint64]*entry)
- filter.fifo = list.New()
- filter.key[0] = binary.BigEndian.Uint64(key[0:8])
- filter.key[1] = binary.BigEndian.Uint64(key[8:16])
- filter.ttl = ttl
-
- return
-}
-
-// TestAndSet queries the filter for a given byte sequence, inserts the
-// sequence, and returns if it was present before the insertion operation.
-func (f *ReplayFilter) TestAndSet(now time.Time, buf []byte) bool {
- digest := siphash.Hash(f.key[0], f.key[1], buf)
-
- f.Lock()
- defer f.Unlock()
-
- f.compactFilter(now)
-
- if e := f.filter[digest]; e != nil {
- // Hit. Just return.
- return true
- }
-
- // Miss. Add a new entry.
- e := new(entry)
- e.digest = digest
- e.firstSeen = now
- e.element = f.fifo.PushBack(e)
- f.filter[digest] = e
-
- return false
-}
-
-func (f *ReplayFilter) compactFilter(now time.Time) {
- e := f.fifo.Front()
- for e != nil {
- ent, _ := e.Value.(*entry)
-
- // If the filter is not full, only purge entries that exceed the TTL,
- // otherwise purge at least one entry, then revert to TTL based
- // compaction.
- if f.fifo.Len() < maxFilterSize && f.ttl > 0 {
- deltaT := now.Sub(ent.firstSeen)
- if deltaT < 0 {
- // Aeeeeeee, the system time jumped backwards, potentially by
- // a lot. This will eventually self-correct, but "eventually"
- // could be a long time. As much as this sucks, jettison the
- // entire filter.
- f.reset()
- return
- } else if deltaT < f.ttl {
- return
- }
- }
-
- // Remove the eldest entry.
- eNext := e.Next()
- delete(f.filter, ent.digest)
- f.fifo.Remove(ent.element)
- ent.element = nil
- e = eNext
- }
-}
-
-func (f *ReplayFilter) reset() {
- f.filter = make(map[uint64]*entry)
- f.fifo = list.New()
-}
diff --git a/common/replayfilter/replay_filter_test.go b/common/replayfilter/replay_filter_test.go
deleted file mode 100644
index 884e4fb..0000000
--- a/common/replayfilter/replay_filter_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 replayfilter
-
-import (
- "testing"
- "time"
-)
-
-func TestReplayFilter(t *testing.T) {
- ttl := 10 * time.Second
-
- f, err := New(ttl)
- if err != nil {
- t.Fatal("newReplayFilter failed:", err)
- }
-
- buf := []byte("This is a test of the Emergency Broadcast System.")
- now := time.Now()
-
- // testAndSet into empty filter, returns false (not present).
- set := f.TestAndSet(now, buf)
- if set {
- t.Fatal("TestAndSet empty filter returned true")
- }
-
- // testAndSet into filter containing entry, should return true(present).
- set = f.TestAndSet(now, buf)
- if !set {
- t.Fatal("testAndSet populated filter (replayed) returned false")
- }
-
- buf2 := []byte("This concludes this test of the Emergency Broadcast System.")
- now = now.Add(ttl)
-
- // testAndSet with time advanced.
- set = f.TestAndSet(now, buf2)
- if set {
- t.Fatal("testAndSet populated filter, 2nd entry returned true")
- }
- set = f.TestAndSet(now, buf2)
- if !set {
- t.Fatal("testAndSet populated filter, 2nd entry (replayed) returned false")
- }
-
- // Ensure that the first entry has been removed by compact.
- set = f.TestAndSet(now, buf)
- if set {
- t.Fatal("testAndSet populated filter, compact check returned true")
- }
-
- // Ensure that the filter gets reaped if the clock jumps backwards.
- now = time.Time{}
- set = f.TestAndSet(now, buf)
- if set {
- t.Fatal("testAndSet populated filter, backward time jump returned true")
- }
- if len(f.filter) != 1 {
- t.Fatal("filter map has a unexpected number of entries:", len(f.filter))
- }
- if f.fifo.Len() != 1 {
- t.Fatal("filter fifo has a unexpected number of entries:", f.fifo.Len())
- }
-
- // Ensure that the entry is properly added after reaping.
- set = f.TestAndSet(now, buf)
- if !set {
- t.Fatal("testAndSet populated filter, post-backward clock jump (replayed) returned false")
- }
-}
diff --git a/common/termmon/termmon.go b/common/termmon/termmon.go
index 0968817..b95c77d 100644
--- a/common/termmon/termmon.go
+++ b/common/termmon/termmon.go
@@ -36,8 +36,8 @@ import (
"syscall"
"time"
- "github.com/OperatorFoundation/obfs4/common/log"
- "github.com/OperatorFoundation/obfs4/common/pt_extras"
+ "github.com/OperatorFoundation/shapeshifter-dispatcher/common/log"
+ "github.com/OperatorFoundation/shapeshifter-dispatcher/common/pt_extras"
)
var TermMonitorOSInit func(*TermMonitor) error
diff --git a/common/uniformdh/uniformdh.go b/common/uniformdh/uniformdh.go
deleted file mode 100644
index ab94a2e..0000000
--- a/common/uniformdh/uniformdh.go
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 uniformdh implements the Tor Project's UniformDH key exchange
-// mechanism as defined in the obfs3 protocol specification. This
-// implementation is suitable for obfuscation but MUST NOT BE USED when strong
-// security is required as it is not constant time.
-package uniformdh
-
-import (
- "fmt"
- "io"
- "math/big"
-)
-
-const (
- // Size is the size of a UniformDH key or shared secret in bytes.
- Size = 1536 / 8
-
- // modpStr is the RFC3526 1536-bit MODP Group (Group 5).
- modpStr = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
- "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
- "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
- "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
- "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
- "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
- "83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
- "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
-
- g = 2
-)
-
-var modpGroup *big.Int
-var gen *big.Int
-
-// A PrivateKey represents a UniformDH private key.
-type PrivateKey struct {
- PublicKey
- privateKey *big.Int
-}
-
-// A PublicKey represents a UniformDH public key.
-type PublicKey struct {
- bytes []byte
- publicKey *big.Int
-}
-
-// Bytes returns the byte representation of a PublicKey.
-func (pub *PublicKey) Bytes() (pubBytes []byte, err error) {
- if len(pub.bytes) != Size || pub.bytes == nil {
- return nil, fmt.Errorf("public key is not initialized")
- }
- pubBytes = make([]byte, Size)
- copy(pubBytes, pub.bytes)
-
- return
-}
-
-// SetBytes sets the PublicKey from a byte slice.
-func (pub *PublicKey) SetBytes(pubBytes []byte) error {
- if len(pubBytes) != Size {
- return fmt.Errorf("public key length %d is not %d", len(pubBytes), Size)
- }
- pub.bytes = make([]byte, Size)
- copy(pub.bytes, pubBytes)
- pub.publicKey = new(big.Int).SetBytes(pub.bytes)
-
- return nil
-}
-
-// GenerateKey generates a UniformDH keypair using the random source random.
-func GenerateKey(random io.Reader) (priv *PrivateKey, err error) {
- privBytes := make([]byte, Size)
- if _, err = io.ReadFull(random, privBytes); err != nil {
- return
- }
- priv, err = generateKey(privBytes)
-
- return
-}
-
-func generateKey(privBytes []byte) (priv *PrivateKey, err error) {
- // This function does all of the actual heavy lifting of creating a public
- // key from a raw 192 byte private key. It is split so that the KAT tests
- // can be written easily, and not exposed since non-ephemeral keys are a
- // terrible idea.
-
- if len(privBytes) != Size {
- return nil, fmt.Errorf("invalid private key size %d", len(privBytes))
- }
-
- // To pick a private UniformDH key, we pick a random 1536-bit number,
- // and make it even by setting its low bit to 0
- privBn := new(big.Int).SetBytes(privBytes)
- wasEven := privBn.Bit(0) == 0
- privBn = privBn.SetBit(privBn, 0, 0)
-
- // Let x be that private key, and X = g^x (mod p).
- pubBn := new(big.Int).Exp(gen, privBn, modpGroup)
- pubAlt := new(big.Int).Sub(modpGroup, pubBn)
-
- // When someone sends her public key to the other party, she randomly
- // decides whether to send X or p-X. Use the lowest most bit of the
- // private key here as the random coin flip since it is masked out and not
- // used.
- //
- // Note: The spec doesn't explicitly specify it, but here we prepend zeros
- // to the key so that it is always exactly Size bytes.
- pubBytes := make([]byte, Size)
- if wasEven {
- err = prependZeroBytes(pubBytes, pubBn.Bytes())
- } else {
- err = prependZeroBytes(pubBytes, pubAlt.Bytes())
- }
- if err != nil {
- return
- }
-
- priv = new(PrivateKey)
- priv.PublicKey.bytes = pubBytes
- priv.PublicKey.publicKey = pubBn
- priv.privateKey = privBn
-
- return
-}
-
-// Handshake generates a shared secret given a PrivateKey and PublicKey.
-func Handshake(privateKey *PrivateKey, publicKey *PublicKey) (sharedSecret []byte, err error) {
- // When a party wants to calculate the shared secret, she raises the
- // foreign public key to her private key.
- secretBn := new(big.Int).Exp(publicKey.publicKey, privateKey.privateKey, modpGroup)
- sharedSecret = make([]byte, Size)
- err = prependZeroBytes(sharedSecret, secretBn.Bytes())
-
- return
-}
-
-func prependZeroBytes(dst, src []byte) error {
- zeros := len(dst) - len(src)
- if zeros < 0 {
- return fmt.Errorf("src length is greater than destination: %d", zeros)
- }
- for i := 0; i < zeros; i++ {
- dst[i] = 0
- }
- copy(dst[zeros:], src)
-
- return nil
-}
-
-func init() {
- // Load the MODP group and the generator.
- var ok bool
- modpGroup, ok = new(big.Int).SetString(modpStr, 16)
- if !ok {
- panic("Failed to load the RFC3526 MODP Group")
- }
- gen = big.NewInt(g)
-}
diff --git a/common/uniformdh/uniformdh_test.go b/common/uniformdh/uniformdh_test.go
deleted file mode 100644
index d705d66..0000000
--- a/common/uniformdh/uniformdh_test.go
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * 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 uniformdh
-
-import (
- "bytes"
- "crypto/rand"
- "encoding/hex"
- "testing"
-)
-
-const (
- xPrivStr = "6f592d676f536874746f20686e6b776f" +
- "20736874206561676574202e6f592d67" +
- "6f536874746f20687369742065686720" +
- "74612e655920676f532d746f6f686874" +
- "6920207368742065656b20796e612064" +
- "7567726169646e616f20206668742065" +
- "61676574202e61507473202c72707365" +
- "6e652c746620747572752c6561206c6c" +
- "612065726f20656e6920206e6f592d67" +
- "6f536874746f2e68482020656e6b776f" +
- "2073687772652065687420656c4f2064" +
- "6e4f736562206f72656b74207268756f"
-
- xPubStr = "76a3d17d5c55b03e865fa3e8267990a7" +
- "24baa24b0bdd0cc4af93be8de30be120" +
- "d5533c91bf63ef923b02edcb84b74438" +
- "3f7de232cca6eb46d07cad83dcaa317f" +
- "becbc68ca13e2c4019e6a36531067450" +
- "04aecc0be1dff0a78733fb0e7d5cb7c4" +
- "97cab77b1331bf347e5f3a7847aa0bc0" +
- "f4bc64146b48407fed7b931d16972d25" +
- "fb4da5e6dc074ce2a58daa8de7624247" +
- "cdf2ebe4e4dfec6d5989aac778c87559" +
- "d3213d6040d4111ce3a2acae19f9ee15" +
- "32509e037f69b252fdc30243cbbce9d0"
-
- yPrivStr = "736562206f72656b74207268756f6867" +
- "6f2020666c6f2c646120646e77206568" +
- "657254206568207968736c61206c7262" +
- "6165206b68746f726775206867616961" +
- "2e6e482020656e6b776f207368777265" +
- "2065685479656820766120657274646f" +
- "652072616874732766206569646c2c73" +
- "6120646e772065686572542065682079" +
- "74736c69206c72746165206468746d65" +
- "202c6e612064687720796f6e6f20656e" +
- "63206e61622068656c6f206468546d65" +
- "61202073685479657420657264610a2e"
-
- yPubStr = "d04e156e554c37ffd7aba749df662350" +
- "1e4ff4466cb12be055617c1a36872237" +
- "36d2c3fdce9ee0f9b27774350849112a" +
- "a5aeb1f126811c9c2f3a9cb13d2f0c3a" +
- "7e6fa2d3bf71baf50d839171534f227e" +
- "fbb2ce4227a38c25abdc5ba7fc430111" +
- "3a2cb2069c9b305faac4b72bf21fec71" +
- "578a9c369bcac84e1a7dcf0754e342f5" +
- "bc8fe4917441b88254435e2abaf297e9" +
- "3e1e57968672d45bd7d4c8ba1bc3d314" +
- "889b5bc3d3e4ea33d4f2dfdd34e5e5a7" +
- "2ff24ee46316d4757dad09366a0b66b3"
-
- ssStr = "78afaf5f457f1fdb832bebc397644a33" +
- "038be9dba10ca2ce4a076f327f3a0ce3" +
- "151d477b869ee7ac467755292ad8a77d" +
- "b9bd87ffbbc39955bcfb03b1583888c8" +
- "fd037834ff3f401d463c10f899aa6378" +
- "445140b7f8386a7d509e7b9db19b677f" +
- "062a7a1a4e1509604d7a0839ccd5da61" +
- "73e10afd9eab6dda74539d60493ca37f" +
- "a5c98cd9640b409cd8bb3be2bc5136fd" +
- "42e764fc3f3c0ddb8db3d87abcf2e659" +
- "8d2b101bef7a56f50ebc658f9df1287d" +
- "a81359543e77e4a4cfa7598a4152e4c0"
-)
-
-var xPriv, xPub, yPriv, yPub, ss []byte
-
-// TestGenerateKeyOdd tests creating a UniformDH keypair with a odd private
-// key.
-func TestGenerateKeyOdd(t *testing.T) {
- xX, err := generateKey(xPriv)
- if err != nil {
- t.Fatal("generateKey(xPriv) failed:", err)
- }
-
- xPubGen, err := xX.PublicKey.Bytes()
- if err != nil {
- t.Fatal("xX.PublicKey.Bytes() failed:", err)
- }
- if 0 != bytes.Compare(xPubGen, xPub) {
- t.Fatal("Generated public key does not match known answer")
- }
-}
-
-// TestGenerateKeyEven tests creating a UniformDH keypair with a even private
-// key.
-func TestGenerateKeyEven(t *testing.T) {
- yY, err := generateKey(yPriv)
- if err != nil {
- t.Fatal("generateKey(yPriv) failed:", err)
- }
-
- yPubGen, err := yY.PublicKey.Bytes()
- if err != nil {
- t.Fatal("yY.PublicKey.Bytes() failed:", err)
- }
- if 0 != bytes.Compare(yPubGen, yPub) {
- t.Fatal("Generated public key does not match known answer")
- }
-}
-
-// TestHandshake tests conductiong a UniformDH handshake with know values.
-func TestHandshake(t *testing.T) {
- xX, err := generateKey(xPriv)
- if err != nil {
- t.Fatal("generateKey(xPriv) failed:", err)
- }
- yY, err := generateKey(yPriv)
- if err != nil {
- t.Fatal("generateKey(yPriv) failed:", err)
- }
-
- xY, err := Handshake(xX, &yY.PublicKey)
- if err != nil {
- t.Fatal("Handshake(xX, yY.PublicKey) failed:", err)
- }
- yX, err := Handshake(yY, &xX.PublicKey)
- if err != nil {
- t.Fatal("Handshake(yY, xX.PublicKey) failed:", err)
- }
-
- if 0 != bytes.Compare(xY, yX) {
- t.Fatal("Generated shared secrets do not match between peers")
- }
- if 0 != bytes.Compare(xY, ss) {
- t.Fatal("Generated shared secret does not match known value")
- }
-}
-
-// Benchmark UniformDH key generation + exchange. THe actual time taken per
-// peer is half of the reported time as this does 2 key generation an
-// handshake operations.
-func BenchmarkHandshake(b *testing.B) {
- for i := 0; i < b.N; i++ {
- xX, err := GenerateKey(rand.Reader)
- if err != nil {
- b.Fatal("Failed to generate xX keypair", err)
- }
-
- yY, err := GenerateKey(rand.Reader)
- if err != nil {
- b.Fatal("Failed to generate yY keypair", err)
- }
-
- xY, err := Handshake(xX, &yY.PublicKey)
- if err != nil {
- b.Fatal("Handshake(xX, yY.PublicKey) failed:", err)
- }
- yX, err := Handshake(yY, &xX.PublicKey)
- if err != nil {
- b.Fatal("Handshake(yY, xX.PublicKey) failed:", err)
- }
-
- _ = xY
- _ = yX
- }
-}
-
-func init() {
- // Load the test vectors into byte slices.
- var err error
- xPriv, err = hex.DecodeString(xPrivStr)
- if err != nil {
- panic("hex.DecodeString(xPrivStr) failed")
- }
- xPub, err = hex.DecodeString(xPubStr)
- if err != nil {
- panic("hex.DecodeString(xPubStr) failed")
- }
- yPriv, err = hex.DecodeString(yPrivStr)
- if err != nil {
- panic("hex.DecodeString(yPrivStr) failed")
- }
- yPub, err = hex.DecodeString(yPubStr)
- if err != nil {
- panic("hex.DecodeString(yPubStr) failed")
- }
- ss, err = hex.DecodeString(ssStr)
- if err != nil {
- panic("hex.DecodeString(ssStr) failed")
- }
-}