Represent Bridge Fingerprint As String

This commit is contained in:
Shelikhoo 2022-05-17 15:53:15 +01:00
parent dd61e2be0f
commit f789dce6d2
No known key found for this signature in database
GPG key ID: C4D5E79D22B25316
7 changed files with 69 additions and 21 deletions

View file

@ -2,27 +2,26 @@ package main
import ( import (
"bufio" "bufio"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
"io" "io"
"sync" "sync"
) )
var ErrBridgeNotFound = errors.New("bridge not found") var ErrBridgeNotFound = errors.New("bridge not found")
var ErrBridgeFingerprintInvalid = errors.New("bridge fingerprint invalid")
func NewBridgeListHolder() BridgeListHolderFileBased { func NewBridgeListHolder() BridgeListHolderFileBased {
return &bridgeListHolder{} return &bridgeListHolder{}
} }
type bridgeListHolder struct { type bridgeListHolder struct {
bridgeInfo map[[20]byte]BridgeInfo bridgeInfo map[bridgefingerprint.Fingerprint]BridgeInfo
accessBridgeInfo sync.RWMutex accessBridgeInfo sync.RWMutex
} }
type BridgeListHolder interface { type BridgeListHolder interface {
GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) GetBridgeInfo(bridgefingerprint.Fingerprint) (BridgeInfo, error)
} }
type BridgeListHolderFileBased interface { type BridgeListHolderFileBased interface {
@ -36,7 +35,7 @@ type BridgeInfo struct {
Fingerprint string `json:"fingerprint"` Fingerprint string `json:"fingerprint"`
} }
func (h *bridgeListHolder) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) { func (h *bridgeListHolder) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprint) (BridgeInfo, error) {
h.accessBridgeInfo.RLock() h.accessBridgeInfo.RLock()
defer h.accessBridgeInfo.RUnlock() defer h.accessBridgeInfo.RUnlock()
if bridgeInfo, ok := h.bridgeInfo[fingerprint]; ok { if bridgeInfo, ok := h.bridgeInfo[fingerprint]; ok {
@ -46,7 +45,7 @@ func (h *bridgeListHolder) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, erro
} }
func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error { func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error {
bridgeInfoMap := map[[20]byte]BridgeInfo{} bridgeInfoMap := map[bridgefingerprint.Fingerprint]BridgeInfo{}
inputScanner := bufio.NewScanner(reader) inputScanner := bufio.NewScanner(reader)
for inputScanner.Scan() { for inputScanner.Scan() {
inputLine := inputScanner.Bytes() inputLine := inputScanner.Bytes()
@ -54,13 +53,14 @@ func (h *bridgeListHolder) LoadBridgeInfo(reader io.Reader) error {
if err := json.Unmarshal(inputLine, &bridgeInfo); err != nil { if err := json.Unmarshal(inputLine, &bridgeInfo); err != nil {
return err return err
} }
var bridgeHash [20]byte
if n, err := hex.Decode(bridgeHash[:], []byte(bridgeInfo.Fingerprint)); err != nil { var bridgeFingerprint bridgefingerprint.Fingerprint
var err error
if bridgeFingerprint, err = bridgefingerprint.FingerprintFromHexString(bridgeInfo.Fingerprint); err != nil {
return err return err
} else if n != 20 {
return ErrBridgeFingerprintInvalid
} }
bridgeInfoMap[bridgeHash] = bridgeInfo
bridgeInfoMap[bridgeFingerprint] = bridgeInfo
} }
h.accessBridgeInfo.Lock() h.accessBridgeInfo.Lock()
defer h.accessBridgeInfo.Unlock() defer h.accessBridgeInfo.Unlock()

View file

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"testing" "testing"
) )
@ -34,7 +35,9 @@ func TestBridgeLoad(t *testing.T) {
So(n, ShouldEqual, 20) So(n, ShouldEqual, 20)
So(err, ShouldBeNil) So(err, ShouldBeNil)
} }
bridgeInfo, err := bridgeList.GetBridgeInfo(bridgeFingerprint) Fingerprint, err := bridgefingerprint.FingerprintFromBytes(bridgeFingerprint[:])
So(err, ShouldBeNil)
bridgeInfo, err := bridgeList.GetBridgeInfo(Fingerprint)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(bridgeInfo.DisplayName, ShouldEqual, "default") So(bridgeInfo.DisplayName, ShouldEqual, "default")
So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://snowflake.torproject.org") So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://snowflake.torproject.org")
@ -50,7 +53,9 @@ func TestBridgeLoad(t *testing.T) {
So(n, ShouldEqual, 20) So(n, ShouldEqual, 20)
So(err, ShouldBeNil) So(err, ShouldBeNil)
} }
bridgeInfo, err := bridgeList.GetBridgeInfo(bridgeFingerprint) Fingerprint, err := bridgefingerprint.FingerprintFromBytes(bridgeFingerprint[:])
So(err, ShouldBeNil)
bridgeInfo, err := bridgeList.GetBridgeInfo(Fingerprint)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(bridgeInfo.DisplayName, ShouldEqual, "imaginary-8") So(bridgeInfo.DisplayName, ShouldEqual, "imaginary-8")
So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://imaginary-8-snowflake.torproject.org") So(bridgeInfo.WebSocketAddress, ShouldEqual, "wss://imaginary-8-snowflake.torproject.org")

View file

@ -10,6 +10,7 @@ import (
"container/heap" "container/heap"
"crypto/tls" "crypto/tls"
"flag" "flag"
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
"io" "io"
"log" "log"
"net/http" "net/http"
@ -44,7 +45,7 @@ type BrokerContext struct {
presumedPatternForLegacyClient string presumedPatternForLegacyClient string
} }
func (ctx *BrokerContext) GetBridgeInfo(fingerprint [20]byte) (BridgeInfo, error) { func (ctx *BrokerContext) GetBridgeInfo(fingerprint bridgefingerprint.Fingerprint) (BridgeInfo, error) {
return ctx.bridgeList.GetBridgeInfo(fingerprint) return ctx.bridgeList.GetBridgeInfo(fingerprint)
} }
@ -178,7 +179,7 @@ func (ctx *BrokerContext) CheckProxyRelayPattern(pattern string, nonSupported bo
type ClientOffer struct { type ClientOffer struct {
natType string natType string
sdp []byte sdp []byte
fingerprint [20]byte fingerprint []byte
} }
func main() { func main() {

View file

@ -4,6 +4,7 @@ import (
"container/heap" "container/heap"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
"log" "log"
"net" "net"
"time" "time"
@ -130,7 +131,11 @@ func (i *IPC) ProxyPolls(arg messages.Arg, response *[]byte) error {
i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "status": "matched"}).Inc() i.ctx.metrics.promMetrics.ProxyPollTotal.With(prometheus.Labels{"nat": natType, "status": "matched"}).Inc()
var relayURL string var relayURL string
if info, err := i.ctx.bridgeList.GetBridgeInfo(offer.fingerprint); err != nil { bridgeFingerprint, err := bridgefingerprint.FingerprintFromBytes(offer.fingerprint)
if err != nil {
return messages.ErrBadRequest
}
if info, err := i.ctx.bridgeList.GetBridgeInfo(bridgeFingerprint); err != nil {
return err return err
} else { } else {
relayURL = info.WebSocketAddress relayURL = info.WebSocketAddress
@ -172,12 +177,18 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
if err != nil { if err != nil {
return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response) return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response)
} }
copy(offer.fingerprint[:], fingerprint)
if _, err := i.ctx.GetBridgeInfo(offer.fingerprint); err != nil { BridgeFingerprint, err := bridgefingerprint.FingerprintFromBytes(fingerprint)
if err != nil {
return sendClientResponse(&messages.ClientPollResponse{Error: err.Error()}, response)
}
if _, err := i.ctx.GetBridgeInfo(BridgeFingerprint); err != nil {
return err return err
} }
offer.fingerprint = BridgeFingerprint.ToBytes()
// Only hand out known restricted snowflakes to unrestricted clients // Only hand out known restricted snowflakes to unrestricted clients
var snowflakeHeap *SnowflakeHeap var snowflakeHeap *SnowflakeHeap
if offer.natType == NATUnrestricted { if offer.natType == NATUnrestricted {

View file

@ -258,7 +258,7 @@ func TestBroker(t *testing.T) {
// Pass a fake client offer to this proxy // Pass a fake client offer to this proxy
p := <-ctx.proxyPolls p := <-ctx.proxyPolls
So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp") So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp")
p.offerChannel <- &ClientOffer{sdp: []byte("fake offer"), fingerprint: defaultBridge} p.offerChannel <- &ClientOffer{sdp: []byte("fake offer"), fingerprint: defaultBridge[:]}
<-done <-done
So(w.Code, ShouldEqual, http.StatusOK) So(w.Code, ShouldEqual, http.StatusOK)
So(w.Body.String(), ShouldEqual, `{"Status":"client match","Offer":"fake offer","NAT":"","RelayURL":"wss://snowflake.torproject.net/"}`) So(w.Body.String(), ShouldEqual, `{"Status":"client match","Offer":"fake offer","NAT":"","RelayURL":"wss://snowflake.torproject.net/"}`)

View file

@ -0,0 +1,30 @@
package bridgefingerprint
import (
"encoding/hex"
"errors"
)
type Fingerprint string
var ErrBridgeFingerprintInvalid = errors.New("bridge fingerprint invalid")
func FingerprintFromBytes(bytes []byte) (Fingerprint, error) {
n := len(bytes)
if n != 20 && n != 32 {
return Fingerprint(""), ErrBridgeFingerprintInvalid
}
return Fingerprint(bytes), nil
}
func FingerprintFromHexString(hexString string) (Fingerprint, error) {
decoded, err := hex.DecodeString(hexString)
if err != nil {
return "", err
}
return FingerprintFromBytes(decoded)
}
func (f Fingerprint) ToBytes() []byte {
return []byte(f)
}

View file

@ -5,9 +5,9 @@ package messages
import ( import (
"bytes" "bytes"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/bridgefingerprint"
"git.torproject.org/pluggable-transports/snowflake.git/v2/common/nat" "git.torproject.org/pluggable-transports/snowflake.git/v2/common/nat"
) )
@ -106,7 +106,8 @@ func DecodeClientPollRequest(data []byte) (*ClientPollRequest, error) {
if message.Fingerprint == "" { if message.Fingerprint == "" {
message.Fingerprint = defaultBridgeFingerprint message.Fingerprint = defaultBridgeFingerprint
} }
if hex.DecodedLen(len(message.Fingerprint)) != 20 {
if _, err := bridgefingerprint.FingerprintFromHexString(message.Fingerprint); err != nil {
return nil, fmt.Errorf("cannot decode fingerprint") return nil, fmt.Errorf("cannot decode fingerprint")
} }