Compare commits
No commits in common. "master" and "bugfix" have entirely different histories.
100
.drone.yml
100
.drone.yml
|
@ -1,73 +1,65 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: linux-test
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/cwtch.im/tapir
|
||||
|
||||
steps:
|
||||
- name: fetch
|
||||
image: golang:1.17.5
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
pipeline:
|
||||
fetch:
|
||||
when:
|
||||
repo: cwtch.im/tapir
|
||||
branch: master
|
||||
event: [ push, pull_request ]
|
||||
image: golang
|
||||
commands:
|
||||
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
|
||||
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc
|
||||
- chmod a+x tor
|
||||
- export GO111MODULE=on
|
||||
- go mod download
|
||||
- go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
- name: quality
|
||||
image: golang:1.17.5
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
- go mod vendor
|
||||
- go get -u golang.org/x/lint/golint
|
||||
quality:
|
||||
when:
|
||||
repo: cwtch.im/tapir
|
||||
branch: master
|
||||
event: [ push, pull_request ]
|
||||
image: golang
|
||||
commands:
|
||||
- staticcheck ./...
|
||||
- name: units-tests
|
||||
image: golang:1.17.5
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
- go list ./... | xargs go vet
|
||||
- go list ./... | xargs golint -set_exit_status
|
||||
units-tests:
|
||||
when:
|
||||
repo: cwtch.im/tapir
|
||||
branch: master
|
||||
event: [ push, pull_request ]
|
||||
image: golang
|
||||
commands:
|
||||
- export PATH=`pwd`:$PATH
|
||||
- export PATH=$PATH:/go/src/cwtch.im/tapir
|
||||
- sh testing/tests.sh
|
||||
- name: integ-test
|
||||
image: golang:1.17.5
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
integ-test:
|
||||
when:
|
||||
repo: cwtch.im/tapir
|
||||
branch: master
|
||||
event: [ push, pull_request ]
|
||||
image: golang
|
||||
commands:
|
||||
- export PATH=`pwd`:$PATH
|
||||
- export PATH=$PATH:/go/src/cwtch.im/tapir
|
||||
- go test -race -v git.openprivacy.ca/cwtch.im/tapir/testing
|
||||
- name: notify-email
|
||||
notify-email:
|
||||
image: drillster/drone-email
|
||||
host: build.openprivacy.ca
|
||||
port: 25
|
||||
skip_verify: true
|
||||
from: drone@openprivacy.ca
|
||||
when:
|
||||
status: [ failure ]
|
||||
- name: notify-gogs
|
||||
image: openpriv/drone-gogs
|
||||
pull: if-not-exists
|
||||
when:
|
||||
event: pull_request
|
||||
status: [ success, changed, failure ]
|
||||
environment:
|
||||
GOGS_ACCOUNT_TOKEN:
|
||||
from_secret: gogs_account_token
|
||||
settings:
|
||||
gogs_url: https://git.openprivacy.ca
|
||||
|
||||
volumes:
|
||||
# gopath where bin and pkg lives to persist across steps
|
||||
- name: deps
|
||||
temp: {}
|
||||
|
||||
trigger:
|
||||
repo: cwtch.im/tapir
|
||||
branch: master
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
- tag
|
||||
event: [ push, pull_request ]
|
||||
status: [ failure ]
|
||||
notify-gogs:
|
||||
image: openpriv/drone-gogs
|
||||
when:
|
||||
repo: cwtch.im/tapir
|
||||
branch: master
|
||||
event: pull_request
|
||||
status: [ success, changed, failure ]
|
||||
secrets: [gogs_account_token]
|
||||
gogs_url: https://git.openprivacy.ca
|
||||
|
|
|
@ -7,4 +7,3 @@ coverage.out
|
|||
*.db
|
||||
/applications/tokenboard/tor/
|
||||
fuzzing/
|
||||
*.cover.out
|
||||
|
|
|
@ -40,17 +40,13 @@ func (appchain *ApplicationChain) NewInstance() tapir.Application {
|
|||
func (appchain *ApplicationChain) Init(connection tapir.Connection) {
|
||||
appchain.TranscriptApp.Init(connection)
|
||||
for i, app := range appchain.apps {
|
||||
// propagate the transcript to the app
|
||||
app.PropagateTranscript(appchain.transcript)
|
||||
// apply the app to the connection
|
||||
connection.SetApp(app)
|
||||
// initialize the application given the connection
|
||||
app.Init(connection)
|
||||
// if we hit our guard then carry on, otherwise close...
|
||||
if !connection.HasCapability(appchain.capabilities[i]) {
|
||||
if connection.HasCapability(appchain.capabilities[i]) == false {
|
||||
connection.Close()
|
||||
return
|
||||
}
|
||||
connection.SetApp(app)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ func (ea *AuthApp) Init(connection tapir.Connection) {
|
|||
connection.SetHostname(assertedHostname)
|
||||
connection.SetCapability(AuthCapability)
|
||||
} else {
|
||||
log.Debugf("Failed Decrypt Challenge: [%x] [%x]\n", remoteChallenge, challengeBytes)
|
||||
log.Errorf("Failed Decrypt Challenge: [%x] [%x]\n", remoteChallenge, challengeBytes)
|
||||
connection.Close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ type MockConnection struct {
|
|||
func (mc *MockConnection) Init(outbound bool) {
|
||||
mc.id, _ = primitives.InitializeEphemeralIdentity()
|
||||
mc.outbound = outbound
|
||||
return
|
||||
}
|
||||
|
||||
func (mc MockConnection) Hostname() string {
|
||||
|
@ -58,9 +59,8 @@ func (MockConnection) SetEncryptionKey(key [32]byte) {
|
|||
// no op
|
||||
}
|
||||
|
||||
func (MockConnection) Send(message []byte) error {
|
||||
func (MockConnection) Send(message []byte) {
|
||||
// no op
|
||||
return nil
|
||||
}
|
||||
|
||||
func (MockConnection) Close() {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"git.openprivacy.ca/cwtch.im/tapir"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
ristretto "github.com/gtank/ristretto255"
|
||||
)
|
||||
|
||||
// ProofOfWorkApplication forces the incoming connection to do proof of work before granting a capability
|
||||
|
@ -67,20 +66,9 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
|
|||
var sum [32]byte
|
||||
solution := []byte{}
|
||||
solve := make([]byte, len(challenge)+32)
|
||||
// reuse our allocation
|
||||
buf := make([]byte, 64)
|
||||
next := new(ristretto.Scalar)
|
||||
encodedSolution := make([]byte, 0, 32)
|
||||
for !solved {
|
||||
err := prng.Next(buf, next)
|
||||
if err != nil {
|
||||
// this will cause the challenge to fail...
|
||||
log.Errorf("error completing challenge: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
//lint:ignore SA1019 API this is "deprecated", but without it it will cause an allocation on every single check
|
||||
solution = next.Encode(encodedSolution)
|
||||
solution = prng.Next().Encode(nil)
|
||||
|
||||
copy(solve[0:], solution[:])
|
||||
copy(solve[len(solution):], challenge[:])
|
||||
|
@ -92,8 +80,6 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
|
|||
solved = false
|
||||
}
|
||||
}
|
||||
// reuse this allocated memory next time...
|
||||
encodedSolution = encodedSolution[:0]
|
||||
}
|
||||
log.Debugf("Validated Challenge %v: %v %v\n", challenge, solution, sum)
|
||||
return solution[:]
|
||||
|
|
|
@ -55,10 +55,7 @@ func (tokenapp *TokenApplication) Init(connection tapir.Connection) {
|
|||
var blinded []privacypass.BlindedToken
|
||||
err := json.Unmarshal(connection.Expect(), &blinded)
|
||||
if err == nil {
|
||||
batchProof, err := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
batchProof := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
|
||||
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
|
||||
data, _ := json.Marshal(batchProof)
|
||||
connection.Send(data)
|
||||
|
|
|
@ -2,6 +2,7 @@ package tokenboard
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"git.openprivacy.ca/cwtch.im/tapir"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/applications"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/networks/tor"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/primitives"
|
||||
|
@ -38,7 +39,8 @@ type FreePaymentHandler struct {
|
|||
|
||||
func (fph *FreePaymentHandler) MakePayment() {
|
||||
id, sk := primitives.InitializeEphemeralIdentity()
|
||||
client := new(tor.BaseOnionService)
|
||||
var client tapir.Service
|
||||
client = new(tor.BaseOnionService)
|
||||
client.Init(fph.ACN, sk, &id)
|
||||
|
||||
tokenApplication := new(applications.TokenApplication)
|
||||
|
@ -74,16 +76,10 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
|
||||
os.MkdirAll("./tor/", 0700)
|
||||
builder := new(torProvider.TorrcBuilder)
|
||||
builder.WithSocksPort(9059).WithControlPort(9060).WithHashedPassword("tapir-integration-test").Build("./tor/torrc")
|
||||
torDataDir := ""
|
||||
var err error
|
||||
if torDataDir, err = os.MkdirTemp("./tor/", "data-dir-"); err != nil {
|
||||
t.Fatalf("could not create data dir")
|
||||
}
|
||||
builder.WithHashedPassword("tapir-integration-test").Build("./tor/torrc")
|
||||
|
||||
// Connect to Tor
|
||||
acn, err := torProvider.NewTorACNWithAuth("./", "", torDataDir, 9060, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
|
||||
|
||||
acn, err := torProvider.NewTorACNWithAuth("./", "", 9051, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
|
||||
if err != nil {
|
||||
t.Fatalf("could not launch ACN %v", err)
|
||||
}
|
||||
|
@ -102,7 +98,8 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
clientAuditableStore.Init(publicsid)
|
||||
|
||||
// Init the Server running the Simple App.
|
||||
service := new(tor.BaseOnionService)
|
||||
var service tapir.Service
|
||||
service = new(tor.BaseOnionService)
|
||||
service.Init(acn, sk, &sid)
|
||||
|
||||
// Goroutine Management
|
||||
|
@ -114,7 +111,8 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
}()
|
||||
|
||||
// Init the Server running the PoW Token App.
|
||||
powTokenService := new(tor.BaseOnionService)
|
||||
var powTokenService tapir.Service
|
||||
powTokenService = new(tor.BaseOnionService)
|
||||
spowid, spowk := primitives.InitializeEphemeralIdentity()
|
||||
powTokenService.Init(acn, spowk, &spowid)
|
||||
sg.Add(1)
|
||||
|
@ -130,7 +128,8 @@ func TestTokenBoardApp(t *testing.T) {
|
|||
|
||||
time.Sleep(time.Second * 60) // wait for server to initialize
|
||||
id, sk := primitives.InitializeEphemeralIdentity()
|
||||
client := new(tor.BaseOnionService)
|
||||
var client tapir.Service
|
||||
client = new(tor.BaseOnionService)
|
||||
client.Init(acn, sk, &id)
|
||||
client.Connect(sid.Hostname(), NewTokenBoardClient(clientAuditableStore, Handler{Store: clientAuditableStore}, &FreePaymentHandler{ACN: acn, TokenService: tokenService, ServerHostname: spowid.Hostname()}))
|
||||
client.WaitForCapabilityOrClose(sid.Hostname(), applications.AuthCapability)
|
||||
|
|
26
go.mod
26
go.mod
|
@ -1,21 +1,15 @@
|
|||
module git.openprivacy.ca/cwtch.im/tapir
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.0.0
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||
github.com/gtank/merlin v0.1.1
|
||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
|
||||
)
|
||||
|
||||
require (
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4 // indirect
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.3
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
|
||||
github.com/gtank/merlin v0.1.1
|
||||
github.com/gtank/ristretto255 v0.1.2
|
||||
github.com/kr/pretty v0.2.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.4
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
|
61
go.sum
61
go.sum
|
@ -1,56 +1,63 @@
|
|||
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6 h1:g74PyDGvpMZ3+K0dXy3mlTJh+e0rcwNk0XF8owzkmOA=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.8.6/go.mod h1:Hn1gpOx/bRZp5wvCtPQVJPXrfeUH0EGiG/Aoa0vjGLg=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.0 h1:c7AANUCrlA4hIqXxIGDOWMtSe8CpDleD1877PShScbM=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.0/go.mod h1:bR0Myx9nm2YzWtsThRelkNMV4Pp7sPDa123O1qsAbVo=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.1 h1:zoM+j7PFj8mQeUCNiDNMe7Uq9dhcJDOhaZcSANfeDL4=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.1/go.mod h1:bR0Myx9nm2YzWtsThRelkNMV4Pp7sPDa123O1qsAbVo=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.2 h1:rQFIjWunLlRmXL5Efsv+7+1cA70T6Uza6RCy2PRm9zc=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.2/go.mod h1:bR0Myx9nm2YzWtsThRelkNMV4Pp7sPDa123O1qsAbVo=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.3 h1:i2Ad/U9FlL9dKr2bhRck7lJ8NoWyGtoEfUwoCyMT0fU=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.3/go.mod h1:bR0Myx9nm2YzWtsThRelkNMV4Pp7sPDa123O1qsAbVo=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.1/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c h1:gkfmnY4Rlt3VINCo4uKdpvngiibQyoENVj5Q88sxXhE=
|
||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c/go.mod h1:tDPFhGdt3hJWqtKwx57i9baiB1Cj0yAg22VOPUqm5vY=
|
||||
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
|
||||
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package tor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
|
@ -23,7 +22,6 @@ type BaseOnionService struct {
|
|||
ls connectivity.ListenService
|
||||
lock sync.Mutex
|
||||
port int
|
||||
shutdownChannel chan bool
|
||||
}
|
||||
|
||||
// Metrics provides a report of useful information about the status of the service e.g. the number of active
|
||||
|
@ -55,25 +53,6 @@ func (s *BaseOnionService) Init(acn connectivity.ACN, sk ed25519.PrivateKey, id
|
|||
s.id = id
|
||||
s.privateKey = sk
|
||||
s.port = 9878
|
||||
// blocking so we can wait on shutdown for closing of this goroutine
|
||||
s.shutdownChannel = make(chan bool)
|
||||
go func() {
|
||||
for s.waitOrTimeout() {
|
||||
s.GarbageCollect()
|
||||
}
|
||||
log.Debugf("closing down garbage collection goroutine")
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *BaseOnionService) waitOrTimeout() bool {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
select {
|
||||
case <-s.shutdownChannel:
|
||||
return false
|
||||
case <-ctx.Done():
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// SetPort configures the port that the service uses.
|
||||
|
@ -84,97 +63,22 @@ func (s *BaseOnionService) SetPort(port int) {
|
|||
// WaitForCapabilityOrClose blocks until the connection has the given capability or the underlying connection is closed
|
||||
// (through error or user action)
|
||||
func (s *BaseOnionService) WaitForCapabilityOrClose(cid string, name tapir.Capability) (tapir.Connection, error) {
|
||||
attempts := 0
|
||||
for {
|
||||
if attempts > 4 {
|
||||
s.connections.Range(func(key, value interface{}) bool {
|
||||
connection := value.(tapir.Connection)
|
||||
if connection.Hostname() == cid {
|
||||
if !connection.IsClosed() {
|
||||
connection.Close()
|
||||
s.connections.Delete(key)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
log.Debugf("WaitForCapabilityOrClose attempts exceeded for %v, all connections closed", cid)
|
||||
return nil, errors.New("failed to acquire capability after multiple attempts, forcibly closing all connections with the peer")
|
||||
}
|
||||
|
||||
if attempts > 0 {
|
||||
// Allow connections to be torn down / closed before checking again
|
||||
// There is no point in busy looping...
|
||||
time.Sleep(time.Second * time.Duration(attempts))
|
||||
}
|
||||
|
||||
// Increment Attempts
|
||||
attempts++
|
||||
|
||||
log.Debugf("Lookup up a connection %v...", cid)
|
||||
// Lookup the connection...
|
||||
conn, err := s.GetConnection(cid)
|
||||
|
||||
// If there are no active connections then return an error...
|
||||
if conn == nil {
|
||||
log.Debugf("no active connection found for %v", cid)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// if we have only one connection and it has the desired capability then return the connection with
|
||||
// no error...
|
||||
// If there is at least one active connection, then check the one that is returned and use it
|
||||
for conn != nil {
|
||||
if conn.HasCapability(name) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
log.Debugf("Found 1 connections for %v, but it lacks the desired capability %v", cid, name)
|
||||
continue
|
||||
}
|
||||
|
||||
// If We have 2 connections for the same hostname...
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
// If we received both a Connection and an Error from GetConnection that means we have multiple connections
|
||||
// If that was the case we refresh from the connection store as the likely case is one of them has been
|
||||
// closed and the other one has the capabilities we need.
|
||||
if err != nil {
|
||||
log.Debugf("found duplicate connections for %v <-> %v %v", s.id.Hostname(), cid, err)
|
||||
|
||||
inboundCount := 0
|
||||
|
||||
// By convention the lowest lexicographical hostname purges all their outbounds to the higher
|
||||
// hostname
|
||||
// Which should only leave a single connection remaining (as we dedupe on connect too)
|
||||
// This does allow people to attempt to guarantee being the outbound connection but due to the bidirectional
|
||||
// authentication this should never result in an advantage in the protocol.
|
||||
// Close all outbound connections to connection
|
||||
s.connections.Range(func(key, value interface{}) bool {
|
||||
connection := value.(tapir.Connection)
|
||||
if connection.Hostname() == cid {
|
||||
if !connection.IsClosed() && connection.IsOutbound() && s.id.Hostname() < cid {
|
||||
connection.Close()
|
||||
s.connections.Delete(key)
|
||||
}
|
||||
|
||||
if !connection.IsClosed() && !connection.IsOutbound() {
|
||||
inboundCount++
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// If we have more than 1 inbound count then forcibly close all connections...
|
||||
// This shouldn't happen honestly, but if it does then it can cause an infinite check here
|
||||
if inboundCount > 1 {
|
||||
s.connections.Range(func(key, value interface{}) bool {
|
||||
connection := value.(tapir.Connection)
|
||||
if connection.Hostname() == cid {
|
||||
if !connection.IsClosed() {
|
||||
connection.Close()
|
||||
s.connections.Delete(key)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return nil, errors.New("multiple inbound connections found and closed; the only resolution to this is to close them all and try connecting again")
|
||||
}
|
||||
conn, err = s.GetConnection(cid)
|
||||
}
|
||||
}
|
||||
// There are no active connections with that hostname
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetConnection returns a connection for a given hostname.
|
||||
|
@ -185,14 +89,11 @@ func (s *BaseOnionService) GetConnection(hostname string) (tapir.Connection, err
|
|||
if connection.Hostname() == hostname {
|
||||
if !connection.IsClosed() {
|
||||
conn = append(conn, connection)
|
||||
} else {
|
||||
// Delete this Closed Connection
|
||||
s.connections.Delete(key)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(conn) == 0 {
|
||||
return nil, errors.New("no connection found")
|
||||
}
|
||||
|
@ -204,26 +105,10 @@ func (s *BaseOnionService) GetConnection(hostname string) (tapir.Connection, err
|
|||
return conn[0], nil
|
||||
}
|
||||
|
||||
// GarbageCollect iterates through the connection pool and cleans up any connections that are closed
|
||||
// that haven't been removed from the map.
|
||||
func (s *BaseOnionService) GarbageCollect() {
|
||||
log.Debugf("running garbage collection...")
|
||||
s.connections.Range(func(key, value interface{}) bool {
|
||||
connection := value.(tapir.Connection)
|
||||
if connection.IsClosed() {
|
||||
// Delete this Closed Connection
|
||||
s.connections.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Connect initializes a new outbound connection to the given peer, using the defined Application
|
||||
func (s *BaseOnionService) Connect(hostname string, app tapir.Application) (bool, error) {
|
||||
currconn, _ := s.GetConnection(hostname)
|
||||
|
||||
// We already have a connection
|
||||
if currconn != nil {
|
||||
_, err := s.GetConnection(hostname)
|
||||
if err == nil {
|
||||
// Note: This check is not 100% reliable. And we may end up with two connections between peers
|
||||
// This can happen when a client connects to a server as the server is connecting to the client
|
||||
// Because at the start of the connection the server cannot derive the true hostname of the client until it
|
||||
|
@ -231,7 +116,6 @@ func (s *BaseOnionService) Connect(hostname string, app tapir.Application) (bool
|
|||
// We mitigate this by performing multiple checks when Connect'ing
|
||||
return true, errors.New("already connected to " + hostname)
|
||||
}
|
||||
|
||||
// connects to a remote server
|
||||
// spins off to a connection struct
|
||||
log.Debugf("Connecting to %v", hostname)
|
||||
|
@ -242,8 +126,8 @@ func (s *BaseOnionService) Connect(hostname string, app tapir.Application) (bool
|
|||
// Second check. If we didn't catch a double connection attempt before the Open we *should* catch it now because
|
||||
// the auth protocol is quick and Open over onion connections can take some time.
|
||||
// Again this isn't 100% reliable.
|
||||
tconn, _ := s.GetConnection(hostname)
|
||||
if tconn != nil {
|
||||
_, err := s.GetConnection(hostname)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
return true, errors.New("already connected to " + hostname)
|
||||
}
|
||||
|
@ -271,10 +155,10 @@ func (s *BaseOnionService) Listen(app tapir.Application) error {
|
|||
s.lock.Lock()
|
||||
ls, err := s.acn.Listen(s.privateKey, s.port)
|
||||
s.ls = ls
|
||||
log.Debugf("Starting a service on %v ", ls.AddressFull())
|
||||
s.lock.Unlock()
|
||||
|
||||
if err == nil {
|
||||
log.Debugf("Starting a service on %v ", s.ls.AddressFull())
|
||||
for {
|
||||
conn, err := s.ls.Accept()
|
||||
if err == nil {
|
||||
|
@ -298,16 +182,11 @@ func (s *BaseOnionService) Shutdown() {
|
|||
if s.ls != nil {
|
||||
s.ls.Close()
|
||||
}
|
||||
|
||||
// close all existing connections manually
|
||||
s.connections.Range(func(key, value interface{}) bool {
|
||||
connection := value.(tapir.Connection)
|
||||
connection.Close()
|
||||
return true
|
||||
})
|
||||
|
||||
// wait for the return of our garbage collection goroutine
|
||||
s.shutdownChannel <- true
|
||||
}
|
||||
|
||||
// Broadcast sends a message to all connections who possess the given capability
|
||||
|
|
|
@ -7,7 +7,8 @@ import (
|
|||
|
||||
func TestBoltPersistence_Open(t *testing.T) {
|
||||
os.Remove("test.dbgi")
|
||||
db := new(BoltPersistence)
|
||||
var db Service
|
||||
db = new(BoltPersistence)
|
||||
db.Open("test.dbgi")
|
||||
db.Setup([]string{"tokens"})
|
||||
// 2020.02: Fails in WSL1 because of a mmap issue.
|
||||
|
|
|
@ -25,6 +25,7 @@ type State struct {
|
|||
Messages []Message
|
||||
}
|
||||
|
||||
//
|
||||
const (
|
||||
auditableDataStoreProtocol = "auditable-data-store"
|
||||
newMessage = "new-message"
|
||||
|
@ -136,7 +137,7 @@ func (as *Store) AppendState(state State) error {
|
|||
// verify that our state matches the servers signed state
|
||||
// this is *not* a security check, as a rogue server can simply sign any state
|
||||
// however committing to a state allows us to build fraud proofs for malicious servers later on.
|
||||
if !ed25519.Verify(as.identity.PublicKey(), as.LatestCommit, state.SignedProof) {
|
||||
if ed25519.Verify(as.identity.PublicKey(), as.LatestCommit, state.SignedProof) == false {
|
||||
return errors.New("state is not consistent, the server is malicious")
|
||||
}
|
||||
return nil
|
||||
|
@ -159,7 +160,7 @@ func (as *Store) MergeState(state State) error {
|
|||
// and not the cause (which could be reordered messages, dropped messages, additional messages or any combination)
|
||||
func (as *Store) VerifyFraudProof(fraudCommit []byte, signedFraudProof SignedProof, key ed25519.PublicKey) (bool, error) {
|
||||
|
||||
if !ed25519.Verify(key, fraudCommit, signedFraudProof) {
|
||||
if ed25519.Verify(key, fraudCommit, signedFraudProof) == false {
|
||||
// This could happen due to misuse of this function (trying to verify a proof with the wrong public key)
|
||||
// This could happen if the server lies to us and submits a fake state proof, however we cannot use this to
|
||||
// prove that the server is acting maliciously
|
||||
|
|
|
@ -2,7 +2,6 @@ package core
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"github.com/gtank/merlin"
|
||||
ristretto "github.com/gtank/ristretto255"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
@ -39,7 +38,7 @@ func (t *Transcript) AddToTranscript(label string, b []byte) {
|
|||
// AddElementToTranscript appends a value to the transcript with the given label
|
||||
// This binds the given data to the label.
|
||||
func (t *Transcript) AddElementToTranscript(label string, element *ristretto.Element) {
|
||||
t.AddToTranscript(label, element.Bytes())
|
||||
t.AddToTranscript(label, element.Encode([]byte{}))
|
||||
}
|
||||
|
||||
// OutputTranscriptToAudit outputs a human-readable copy of the transcript so far.
|
||||
|
@ -68,14 +67,12 @@ type PRNG struct {
|
|||
}
|
||||
|
||||
// Next returns the next "random" scalar from the PRNG
|
||||
func (prng *PRNG) Next(buf []byte, next *ristretto.Scalar) error {
|
||||
n, err := io.ReadFull(prng.prng, buf)
|
||||
if n != 64 || err != nil {
|
||||
log.Errorf("could not read prng: %v %v", n, err)
|
||||
return fmt.Errorf("error fetching complete output from prng: %v", err)
|
||||
}
|
||||
next.SetUniformBytes(buf)
|
||||
return nil
|
||||
func (prng *PRNG) Next() *ristretto.Scalar {
|
||||
buf := [64]byte{}
|
||||
io.ReadFull(prng.prng, buf[:])
|
||||
next := new(ristretto.Scalar)
|
||||
next.FromUniformBytes(buf[:])
|
||||
return next
|
||||
}
|
||||
|
||||
// CommitToPRNG commits the label to the transcript and derives a PRNG from the transcript.
|
||||
|
@ -89,8 +86,7 @@ func (t *Transcript) CommitToPRNG(label string) PRNG {
|
|||
// CommitToGenerator derives a verifiably random generator from the transcript
|
||||
func (t *Transcript) CommitToGenerator(label string) *ristretto.Element {
|
||||
c := t.CommitToTranscript(label)
|
||||
result, _ := new(ristretto.Element).SetUniformBytes(c)
|
||||
return result
|
||||
return new(ristretto.Element).FromUniformBytes(c)
|
||||
}
|
||||
|
||||
// CommitToGenerators derives a set of verifiably random generators from the transcript
|
||||
|
@ -105,6 +101,6 @@ func (t *Transcript) CommitToGenerators(label string, n int) (generators []*rist
|
|||
func (t *Transcript) CommitToTranscriptScalar(label string) *ristretto.Scalar {
|
||||
c := t.CommitToTranscript(label)
|
||||
s := new(ristretto.Scalar)
|
||||
s.SetUniformBytes(c[:])
|
||||
s.FromUniformBytes(c[:])
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ func TestNewTranscript(t *testing.T) {
|
|||
|
||||
transcript.AddToTranscript("action", []byte("test data"))
|
||||
|
||||
firstAudit := transcript.OutputTranscriptToAudit()
|
||||
secondAudit := transcript.OutputTranscriptToAudit()
|
||||
if firstAudit != secondAudit {
|
||||
if transcript.OutputTranscriptToAudit() != transcript.OutputTranscriptToAudit() {
|
||||
t.Fatalf("Multiple Audit Calls should not impact underlying Transcript")
|
||||
}
|
||||
t.Logf("%v", transcript.OutputTranscriptToAudit())
|
||||
|
|
|
@ -16,7 +16,6 @@ type DLEQProof struct {
|
|||
// DiscreteLogEquivalenceProof constructs a valid DLEQProof for the given parameters and transcript
|
||||
// Given Y = kX & Q = kP
|
||||
// Peggy: t := choose randomly from Zq
|
||||
//
|
||||
// A := tX
|
||||
// B := tP
|
||||
// c := H(transcript(X,Y,P,Q,A,B))
|
||||
|
@ -26,19 +25,17 @@ type DLEQProof struct {
|
|||
func DiscreteLogEquivalenceProof(k *ristretto.Scalar, X *ristretto.Element, Y *ristretto.Element, P *ristretto.Element, Q *ristretto.Element, transcript *core.Transcript) DLEQProof {
|
||||
private := make([]byte, 64)
|
||||
rand.Read(private)
|
||||
t, err := new(ristretto.Scalar).SetUniformBytes(private)
|
||||
if err != nil {
|
||||
return DLEQProof{ristretto.NewScalar(), ristretto.NewScalar()}
|
||||
}
|
||||
t := new(ristretto.Scalar)
|
||||
t.FromUniformBytes(private)
|
||||
A := new(ristretto.Element).ScalarMult(t, X)
|
||||
B := new(ristretto.Element).ScalarMult(t, P)
|
||||
|
||||
transcript.AddToTranscript(DLEQX, X.Bytes())
|
||||
transcript.AddToTranscript(DLEQY, Y.Bytes())
|
||||
transcript.AddToTranscript(DLEQP, P.Bytes())
|
||||
transcript.AddToTranscript(DLEQQ, Q.Bytes())
|
||||
transcript.AddToTranscript(DLEQA, A.Bytes())
|
||||
transcript.AddToTranscript(DLEQB, B.Bytes())
|
||||
transcript.AddToTranscript(DLEQX, X.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQY, Y.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQP, P.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQQ, Q.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQA, A.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQB, B.Encode(nil))
|
||||
|
||||
c := transcript.CommitToTranscriptScalar("c")
|
||||
s := new(ristretto.Scalar).Subtract(t, new(ristretto.Scalar).Multiply(c, k))
|
||||
|
@ -48,14 +45,12 @@ func DiscreteLogEquivalenceProof(k *ristretto.Scalar, X *ristretto.Element, Y *r
|
|||
// VerifyDiscreteLogEquivalenceProof verifies the DLEQ for the given parameters and transcript
|
||||
// Given Y = kX & Q = kP and Proof = (c,s)
|
||||
// Vicky: X' := sX
|
||||
//
|
||||
// Y' := cY
|
||||
// P' := sP
|
||||
// Q' := cQ
|
||||
// A' = X'+Y' == sX + cY ?= sG + ckG == (s+ck)X == tX == A
|
||||
// B' = P'+Q' == sP + cQ ?= sP + ckP == (s+ck)P == tP == B
|
||||
// c' := H(transcript(X,Y,P,Q,A',B'))
|
||||
//
|
||||
// Tests c ?= c
|
||||
func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Element, Y *ristretto.Element, P *ristretto.Element, Q *ristretto.Element, transcript *core.Transcript) bool {
|
||||
|
||||
|
@ -67,12 +62,12 @@ func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Element, Y *
|
|||
A := new(ristretto.Element).Add(Xs, Yc)
|
||||
B := new(ristretto.Element).Add(Ps, Qc)
|
||||
|
||||
transcript.AddToTranscript(DLEQX, X.Bytes())
|
||||
transcript.AddToTranscript(DLEQY, Y.Bytes())
|
||||
transcript.AddToTranscript(DLEQP, P.Bytes())
|
||||
transcript.AddToTranscript(DLEQQ, Q.Bytes())
|
||||
transcript.AddToTranscript(DLEQA, A.Bytes())
|
||||
transcript.AddToTranscript(DLEQB, B.Bytes())
|
||||
transcript.AddToTranscript(DLEQX, X.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQY, Y.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQP, P.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQQ, Q.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQA, A.Encode(nil))
|
||||
transcript.AddToTranscript(DLEQB, B.Encode(nil))
|
||||
|
||||
return transcript.CommitToTranscriptScalar("c").Equal(dleq.C) == 1
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package privacypass
|
|||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
|
@ -56,10 +55,10 @@ func (t *Token) GenBlindedToken() BlindedToken {
|
|||
t.r = new(ristretto.Scalar)
|
||||
b := make([]byte, 64)
|
||||
rand.Read(b)
|
||||
t.r.SetUniformBytes(b)
|
||||
t.r.FromUniformBytes(b)
|
||||
|
||||
Ht := sha3.Sum512(t.t)
|
||||
T, _ := new(ristretto.Element).SetUniformBytes(Ht[:])
|
||||
T := new(ristretto.Element).FromUniformBytes(Ht[:])
|
||||
P := new(ristretto.Element).ScalarMult(t.r, T)
|
||||
return BlindedToken{P}
|
||||
}
|
||||
|
@ -71,7 +70,7 @@ func (t *Token) unblindSignedToken(token SignedToken) {
|
|||
|
||||
// SpendToken binds the token with data and then redeems the token
|
||||
func (t *Token) SpendToken(data []byte) SpentToken {
|
||||
key := sha3.Sum256(append(t.t, t.W.Bytes()...))
|
||||
key := sha3.Sum256(append(t.t, t.W.Encode(nil)...))
|
||||
mac := hmac.New(sha3.New512, key[:])
|
||||
mac.Write(data)
|
||||
return SpentToken{t.t, mac.Sum(nil)}
|
||||
|
@ -89,25 +88,19 @@ func GenerateBlindedTokenBatch(num int) (tokens []*Token, blindedTokens []Blinde
|
|||
// verifyBatchProof verifies a given batch proof (see also UnblindSignedTokenBatch)
|
||||
func verifyBatchProof(dleq DLEQProof, Y *ristretto.Element, blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) bool {
|
||||
transcript.NewProtocol(BatchProofProtocol)
|
||||
transcript.AddToTranscript(BatchProofX, ristretto.NewGeneratorElement().Bytes())
|
||||
transcript.AddToTranscript(BatchProofY, Y.Bytes())
|
||||
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofY, Y.Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
|
||||
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
|
||||
prng := transcript.CommitToPRNG("w")
|
||||
M := ristretto.NewIdentityElement()
|
||||
Z := ristretto.NewIdentityElement()
|
||||
buf := make([]byte, 64)
|
||||
c := new(ristretto.Scalar)
|
||||
M := new(ristretto.Element).Zero()
|
||||
Z := new(ristretto.Element).Zero()
|
||||
for i := range blindedTokens {
|
||||
err := prng.Next(buf, c)
|
||||
if err != nil {
|
||||
log.Errorf("error verifying batch proof: %v", err)
|
||||
return false
|
||||
}
|
||||
c := prng.Next()
|
||||
M = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, blindedTokens[i].P), M)
|
||||
Z = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, signedTokens[i].Q), Z)
|
||||
}
|
||||
return VerifyDiscreteLogEquivalenceProof(dleq, ristretto.NewGeneratorElement(), Y, M, Z, transcript)
|
||||
return VerifyDiscreteLogEquivalenceProof(dleq, new(ristretto.Element).Base(), Y, M, Z, transcript)
|
||||
}
|
||||
|
||||
// UnblindSignedTokenBatch taking in a set of tokens, their blinded & signed counterparts, a server public key (Y), a DLEQ proof and a transcript
|
||||
|
@ -123,16 +116,3 @@ func UnblindSignedTokenBatch(tokens []*Token, blindedTokens []BlindedToken, sign
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MarshalJSON - in order to store tokens in a serialized form we need to expose the private, unexported value
|
||||
// `t`. Note that `r` is not needed to spend the token, and as such we effectively destroy it when we serialize.
|
||||
// Ideally, go would let us do this with an annotation, alas.
|
||||
func (t Token) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
T []byte `json:"t"`
|
||||
W *ristretto.Element
|
||||
}{
|
||||
T: t.t,
|
||||
W: t.W,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -52,16 +52,11 @@ func TestToken_ConstrainToToken(t *testing.T) {
|
|||
token2 := new(Token)
|
||||
blindedToken2 := token2.GenBlindedToken()
|
||||
Ht := sha3.Sum512(token.t)
|
||||
T, _ := new(ristretto255.Element).SetUniformBytes(Ht[:])
|
||||
T := new(ristretto255.Element).FromUniformBytes(Ht[:])
|
||||
// Constraint forces T = kW to be part of the batch proof
|
||||
// And because the batch proof must prove that *all* inputs share the same key and also checks the servers public key
|
||||
// We get a consistency check for almost free.
|
||||
signedTokens, err := server.SignBlindedTokenBatchWithConstraint([]BlindedToken{blindedToken2}, token.t, core.NewTranscript(""))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("error signing tokens with constraints")
|
||||
}
|
||||
|
||||
signedTokens := server.SignBlindedTokenBatchWithConstraint([]BlindedToken{blindedToken2}, token.t, core.NewTranscript(""))
|
||||
transcript := core.NewTranscript("")
|
||||
|
||||
// NOTE: For this to work token.t and token.W need to be obtain by the client from known source e.g. a public message board.
|
||||
|
@ -75,7 +70,7 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
|
|||
db.Open("tokens.db")
|
||||
|
||||
fakeRand := sha512.Sum512([]byte{})
|
||||
k, _ := ristretto255.NewScalar().SetUniformBytes(fakeRand[:])
|
||||
k := ristretto255.NewScalar().FromUniformBytes(fakeRand[:])
|
||||
server := NewTokenServerFromStore(k, db)
|
||||
defer server.Close()
|
||||
|
||||
|
@ -83,10 +78,7 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
|
|||
serverTranscript := core.NewTranscript("privacyPass")
|
||||
|
||||
tokens, blindedTokens := GenerateBlindedTokenBatch(10)
|
||||
batchProof, err := server.SignBlindedTokenBatch(blindedTokens, serverTranscript)
|
||||
if err != nil {
|
||||
t.Fatalf("error constructing signed/blinded token batch: %v", err)
|
||||
}
|
||||
batchProof := server.SignBlindedTokenBatch(blindedTokens, serverTranscript)
|
||||
|
||||
verified := UnblindSignedTokenBatch(tokens, blindedTokens, batchProof.SignedTokens, server.Y, batchProof.Proof, clientTranscript)
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"fmt"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/persistence"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
ristretto "github.com/gtank/ristretto255"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"sync"
|
||||
|
@ -39,7 +38,7 @@ func NewTokenServer() *TokenServer {
|
|||
// unable to generate secure random numbers
|
||||
panic("unable to generate secure random numbers")
|
||||
}
|
||||
k.SetUniformBytes(b)
|
||||
k.FromUniformBytes(b)
|
||||
return &TokenServer{k, new(ristretto.Element).ScalarBaseMult(k), make(map[string]bool), nil, sync.Mutex{}}
|
||||
}
|
||||
|
||||
|
@ -70,68 +69,47 @@ func (ts *TokenServer) SignBlindedToken(bt BlindedToken) SignedToken {
|
|||
}
|
||||
|
||||
// SignBlindedTokenBatch signs a batch of blinded tokens under a given transcript
|
||||
func (ts *TokenServer) SignBlindedTokenBatch(blindedTokens []BlindedToken, transcript *core.Transcript) (*SignedBatchWithProof, error) {
|
||||
func (ts *TokenServer) SignBlindedTokenBatch(blindedTokens []BlindedToken, transcript *core.Transcript) SignedBatchWithProof {
|
||||
var signedTokens []SignedToken
|
||||
for _, bt := range blindedTokens {
|
||||
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
|
||||
}
|
||||
|
||||
proof, err := ts.constructBatchProof(blindedTokens, signedTokens, transcript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signedProof := SignedBatchWithProof{signedTokens, *proof}
|
||||
return &signedProof, nil
|
||||
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, signedTokens, transcript)}
|
||||
}
|
||||
|
||||
// SignBlindedTokenBatchWithConstraint signs a batch of blinded tokens under a given transcript given a constraint that the tokens must be signed
|
||||
// by the same public key as an existing token
|
||||
func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []BlindedToken, constraintToken []byte, transcript *core.Transcript) (*SignedBatchWithProof, error) {
|
||||
func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []BlindedToken, constraintToken []byte, transcript *core.Transcript) SignedBatchWithProof {
|
||||
var signedTokens []SignedToken
|
||||
for _, bt := range blindedTokens {
|
||||
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
|
||||
}
|
||||
Ht := sha3.Sum512(constraintToken)
|
||||
T, err := new(ristretto.Element).SetUniformBytes(Ht[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
T := new(ristretto.Element).FromUniformBytes(Ht[:])
|
||||
// W == kT
|
||||
W := new(ristretto.Element).ScalarMult(ts.k, T)
|
||||
blindedTokens = append(blindedTokens, BlindedToken{P: T})
|
||||
proof, err := ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: W}), transcript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signedProof := SignedBatchWithProof{signedTokens, *proof}
|
||||
return &signedProof, nil
|
||||
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: W}), transcript)}
|
||||
}
|
||||
|
||||
// constructBatchProof construct a batch proof that all the signed tokens have been signed correctly
|
||||
func (ts *TokenServer) constructBatchProof(blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) (*DLEQProof, error) {
|
||||
func (ts *TokenServer) constructBatchProof(blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) DLEQProof {
|
||||
transcript.NewProtocol(BatchProofProtocol)
|
||||
transcript.AddToTranscript(BatchProofX, ristretto.NewGeneratorElement().Bytes())
|
||||
transcript.AddToTranscript(BatchProofY, ts.Y.Bytes())
|
||||
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofY, ts.Y.Encode(nil))
|
||||
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
|
||||
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
|
||||
prng := transcript.CommitToPRNG("w")
|
||||
|
||||
M := ristretto.NewIdentityElement()
|
||||
Z := ristretto.NewIdentityElement()
|
||||
M := new(ristretto.Element).Zero()
|
||||
Z := new(ristretto.Element).Zero()
|
||||
|
||||
buf := make([]byte, 64)
|
||||
c := new(ristretto.Scalar)
|
||||
for i := range blindedTokens {
|
||||
err := prng.Next(buf, c)
|
||||
if err != nil {
|
||||
log.Errorf("error constructing batch proof: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
c := prng.Next()
|
||||
M = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, blindedTokens[i].P), M)
|
||||
Z = new(ristretto.Element).Add(new(ristretto.Element).ScalarMult(c, signedTokens[i].Q), Z)
|
||||
}
|
||||
proof := DiscreteLogEquivalenceProof(ts.k, ristretto.NewGeneratorElement(), ts.Y, M, Z, transcript)
|
||||
return &proof, nil
|
||||
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Element).Base(), ts.Y, M, Z, transcript)
|
||||
}
|
||||
|
||||
// SpendToken returns true a SpentToken is valid and has never been spent before, false otherwise.
|
||||
|
@ -144,22 +122,19 @@ func (ts *TokenServer) SpendToken(token SpentToken, data []byte) error {
|
|||
}
|
||||
} else {
|
||||
spent, err := ts.persistanceService.Check(tokenBucket, hex.EncodeToString(token.T))
|
||||
if err != nil || spent {
|
||||
if err != nil || spent == true {
|
||||
return fmt.Errorf("token: %v has already been spent", token)
|
||||
}
|
||||
}
|
||||
Ht := sha3.Sum512(token.T)
|
||||
T, err := new(ristretto.Element).SetUniformBytes(Ht[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
T := new(ristretto.Element).FromUniformBytes(Ht[:])
|
||||
W := new(ristretto.Element).ScalarMult(ts.k, T)
|
||||
key := sha3.Sum256(append(token.T, W.Bytes()...))
|
||||
key := sha3.Sum256(append(token.T, W.Encode(nil)...))
|
||||
mac := hmac.New(sha3.New512, key[:])
|
||||
mac.Write(data)
|
||||
computedMAC := mac.Sum(nil)
|
||||
result := hmac.Equal(token.MAC, computedMAC)
|
||||
if result {
|
||||
if result == true {
|
||||
if ts.persistanceService == nil {
|
||||
ts.seen[hex.EncodeToString(token.T)] = true
|
||||
} else {
|
||||
|
|
29
service.go
29
service.go
|
@ -3,7 +3,6 @@ package tapir
|
|||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"git.openprivacy.ca/cwtch.im/tapir/primitives"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
|
@ -40,7 +39,7 @@ type Connection interface {
|
|||
HasCapability(name Capability) bool
|
||||
SetCapability(name Capability)
|
||||
SetEncryptionKey(key [32]byte)
|
||||
Send(message []byte) error
|
||||
Send(message []byte)
|
||||
Close()
|
||||
App() Application
|
||||
SetApp(application Application)
|
||||
|
@ -62,7 +61,6 @@ type connection struct {
|
|||
MaxLength int
|
||||
lock sync.Mutex
|
||||
service Service
|
||||
expectBuffer []byte
|
||||
}
|
||||
|
||||
// NewConnection creates a new Connection
|
||||
|
@ -75,7 +73,7 @@ func NewConnection(service Service, id *primitives.Identity, hostname string, ou
|
|||
connection.outbound = outbound
|
||||
connection.MaxLength = 8192
|
||||
connection.service = service
|
||||
connection.expectBuffer = make([]byte, 8192)
|
||||
|
||||
go connection.app.Init(connection)
|
||||
return connection
|
||||
}
|
||||
|
@ -154,9 +152,10 @@ func (c *connection) closeInner() {
|
|||
|
||||
// Expect blocks and reads a single Tapir packet , from the connection.
|
||||
func (c *connection) Expect() []byte {
|
||||
buffer := make([]byte, c.MaxLength)
|
||||
// Multiple goroutines may invoke methods on a Conn simultaneously.
|
||||
// As such we don't need to mutex around closed.
|
||||
n, err := io.ReadFull(c.conn, c.expectBuffer)
|
||||
n, err := io.ReadFull(c.conn, buffer)
|
||||
|
||||
if n != c.MaxLength || err != nil {
|
||||
log.Debugf("[%v -> %v] Wire Error Reading, Read %d bytes, Error: %v", c.hostname, c.identity.Hostname(), n, err)
|
||||
|
@ -167,22 +166,22 @@ func (c *connection) Expect() []byte {
|
|||
defer c.lock.Unlock()
|
||||
if c.encrypted {
|
||||
var decryptNonce [24]byte
|
||||
copy(decryptNonce[:], c.expectBuffer[:24])
|
||||
decrypted, ok := secretbox.Open(nil, c.expectBuffer[24:], &decryptNonce, &c.key)
|
||||
copy(decryptNonce[:], buffer[:24])
|
||||
decrypted, ok := secretbox.Open(nil, buffer[24:], &decryptNonce, &c.key)
|
||||
if ok {
|
||||
copy(c.expectBuffer, decrypted)
|
||||
copy(buffer, decrypted)
|
||||
} else {
|
||||
log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.hostname, c.identity.Hostname())
|
||||
c.closeInner()
|
||||
return []byte{}
|
||||
}
|
||||
}
|
||||
length, _ := binary.Uvarint(c.expectBuffer[0:2])
|
||||
length, _ := binary.Uvarint(buffer[0:2])
|
||||
if length+2 >= uint64(c.MaxLength) {
|
||||
return []byte{}
|
||||
}
|
||||
//cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer)
|
||||
return c.expectBuffer[2 : length+2]
|
||||
return buffer[2 : length+2]
|
||||
}
|
||||
|
||||
// SetEncryptionKey turns on application-level encryption on the connection using the given key.
|
||||
|
@ -194,13 +193,7 @@ func (c *connection) SetEncryptionKey(key [32]byte) {
|
|||
}
|
||||
|
||||
// Send writes a given message to a Tapir packet (of 1024 bytes in length).
|
||||
func (c *connection) Send(message []byte) error {
|
||||
|
||||
// We can only encode messages up to maxLength
|
||||
if len(message) >= c.MaxLength {
|
||||
log.Errorf("attempting to send a message that is too big")
|
||||
return errors.New("message too long")
|
||||
}
|
||||
func (c *connection) Send(message []byte) {
|
||||
|
||||
buffer := make([]byte, c.MaxLength)
|
||||
binary.PutUvarint(buffer[0:2], uint64(len(message)))
|
||||
|
@ -213,7 +206,6 @@ func (c *connection) Send(message []byte) error {
|
|||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
||||
log.Errorf("Could not read sufficient randomness %v. Closing connection", err)
|
||||
c.closeInner()
|
||||
return errors.New("could not read random")
|
||||
}
|
||||
// MaxLength - 40 = MaxLength - 24 nonce bytes and 16 auth tag.
|
||||
encrypted := secretbox.Seal(nonce[:], buffer[0:c.MaxLength-40], &nonce, &c.key)
|
||||
|
@ -224,7 +216,6 @@ func (c *connection) Send(message []byte) error {
|
|||
if err != nil {
|
||||
c.closeInner()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Broadcast sends a message to all active service connections with a given capability
|
||||
|
|
|
@ -9,7 +9,7 @@ go list ./... | xargs go vet
|
|||
echo ""
|
||||
echo "Linting:"
|
||||
|
||||
staticcheck ./...
|
||||
go list ./... | xargs golint
|
||||
|
||||
|
||||
echo "Time to format"
|
||||
|
@ -21,4 +21,4 @@ ineffassign .
|
|||
|
||||
# misspell (https://github.com/client9/misspell/cmd/misspell)
|
||||
echo "Checking for misspelled words..."
|
||||
misspell . | grep -v "testing/" | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"
|
||||
misspell . | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"golang.org/x/crypto/ed25519"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -64,16 +63,10 @@ func TestTapir(t *testing.T) {
|
|||
// Connect to Tor
|
||||
os.MkdirAll("./tor/", 0700)
|
||||
builder := new(torProvider.TorrcBuilder)
|
||||
builder.WithSocksPort(9059).WithControlPort(9060).WithHashedPassword("tapir-integration-test").Build("./tor/torrc")
|
||||
|
||||
torDataDir := ""
|
||||
var err error
|
||||
if torDataDir, err = os.MkdirTemp("./tor/", "data-dir-"); err != nil {
|
||||
t.Fatalf("could not create data dir")
|
||||
}
|
||||
builder.WithHashedPassword("tapir-integration-test").Build("./tor/torrc")
|
||||
|
||||
// Connect to Tor
|
||||
acn, err := torProvider.NewTorACNWithAuth("./", "", torDataDir, 9060, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
|
||||
acn, err := torProvider.NewTorACNWithAuth("./", "", 9051, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
|
||||
if err != nil {
|
||||
t.Fatalf("could not launch ACN %v", err)
|
||||
}
|
||||
|
@ -83,7 +76,8 @@ func TestTapir(t *testing.T) {
|
|||
id, sk := primitives.InitializeEphemeralIdentity()
|
||||
|
||||
// Init the Server running the Simple App.
|
||||
service := new(tor.BaseOnionService)
|
||||
var service tapir.Service
|
||||
service = new(tor.BaseOnionService)
|
||||
service.Init(acn, sk, &id)
|
||||
|
||||
// Goroutine Management
|
||||
|
@ -100,22 +94,17 @@ func TestTapir(t *testing.T) {
|
|||
wg.Add(2)
|
||||
// Init a Client to Connect to the Server
|
||||
client, clienthostname := genclient(acn)
|
||||
go connectclient(t, client, id.PublicKey(), wg)
|
||||
go connectclient(client, id.PublicKey(), wg)
|
||||
CheckConnection(service, clienthostname, wg)
|
||||
wg.Wait()
|
||||
// Wait for Garbage Collection...
|
||||
time.Sleep(time.Second * 60)
|
||||
// Wait for Server to Sync
|
||||
time.Sleep(time.Second * 2)
|
||||
log.Infof("Closing ACN...")
|
||||
client.Shutdown()
|
||||
service.Shutdown()
|
||||
acn.Close()
|
||||
sg.Wait()
|
||||
time.Sleep(time.Second * 5)
|
||||
time.Sleep(time.Second * 2)
|
||||
log.Infof("Number of goroutines open at close: %d", runtime.NumGoroutine())
|
||||
if numRoutinesStart != runtime.NumGoroutine() {
|
||||
|
||||
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
||||
|
||||
t.Errorf("Potential goroutine leak: Num Start:%v NumEnd: %v", numRoutinesStart, runtime.NumGoroutine())
|
||||
}
|
||||
if !AuthSuccess {
|
||||
|
@ -126,13 +115,14 @@ func TestTapir(t *testing.T) {
|
|||
|
||||
func genclient(acn connectivity.ACN) (tapir.Service, string) {
|
||||
id, sk := primitives.InitializeEphemeralIdentity()
|
||||
client := new(tor.BaseOnionService)
|
||||
var client tapir.Service
|
||||
client = new(tor.BaseOnionService)
|
||||
client.Init(acn, sk, &id)
|
||||
return client, id.Hostname()
|
||||
}
|
||||
|
||||
// Client will Connect and launch it's own Echo App goroutine.
|
||||
func connectclient(t *testing.T, client tapir.Service, key ed25519.PublicKey, group *sync.WaitGroup) {
|
||||
func connectclient(client tapir.Service, key ed25519.PublicKey, group *sync.WaitGroup) {
|
||||
client.Connect(torProvider.GetTorV3Hostname(key), new(SimpleApp))
|
||||
|
||||
// Once connected, it shouldn't take long to authenticate and run the application. So for the purposes of this demo
|
||||
|
@ -141,18 +131,6 @@ func connectclient(t *testing.T, client tapir.Service, key ed25519.PublicKey, gr
|
|||
|
||||
conn, _ := client.GetConnection(torProvider.GetTorV3Hostname(key))
|
||||
log.Debugf("Client has Auth: %v", conn.HasCapability(applications.AuthCapability))
|
||||
|
||||
if conn.HasCapability(applications.AuthCapability) == false {
|
||||
t.Errorf("tapir auth failed")
|
||||
}
|
||||
|
||||
// attempt to send a message that is too long
|
||||
var long [8195]byte
|
||||
err := conn.Send(long[:])
|
||||
if err == nil {
|
||||
t.Errorf("should have errored on message being too long...")
|
||||
}
|
||||
|
||||
AuthSuccess = true
|
||||
group.Done()
|
||||
}
|
||||
|
|
|
@ -26,15 +26,7 @@ func TestTapirMaliciousRemote(t *testing.T) {
|
|||
builder.WithHashedPassword("tapir-integration-test").Build("./tor/torrc")
|
||||
|
||||
// Connect to Tor
|
||||
|
||||
torDataDir := ""
|
||||
var err error
|
||||
if torDataDir, err = os.MkdirTemp("./tor/", "data-dir-"); err != nil {
|
||||
t.Fatalf("could not create data dir")
|
||||
}
|
||||
|
||||
// Connect to Tor
|
||||
acn, err := torProvider.NewTorACNWithAuth("./", "", torDataDir, 9051, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
|
||||
acn, err := torProvider.NewTorACNWithAuth("./", "", 9051, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
|
||||
if err != nil {
|
||||
t.Fatalf("could not launch ACN %v", err)
|
||||
}
|
||||
|
@ -45,7 +37,8 @@ func TestTapirMaliciousRemote(t *testing.T) {
|
|||
id2, sk2 := primitives.InitializeEphemeralIdentity()
|
||||
|
||||
// Init the Server running the Simple App.
|
||||
service := new(tor.BaseOnionService)
|
||||
var service tapir.Service
|
||||
service = new(tor.BaseOnionService)
|
||||
// Initialize an onion service with one identity, but the auth app with another, this should
|
||||
// trigger a failure in authentication protocol
|
||||
service.Init(acn, sk2, &id)
|
||||
|
@ -70,8 +63,6 @@ func TestTapirMaliciousRemote(t *testing.T) {
|
|||
// Wait for Server to Sync
|
||||
time.Sleep(time.Second * 2)
|
||||
log.Infof("closing ACN...")
|
||||
client.Shutdown()
|
||||
service.Shutdown()
|
||||
acn.Close()
|
||||
sg.Wait()
|
||||
time.Sleep(time.Second * 5) // wait for goroutines to finish...
|
||||
|
@ -93,7 +84,7 @@ func connectclientandfail(client tapir.Service, key ed25519.PublicKey, group *sy
|
|||
conn, err := client.GetConnection(torProvider.GetTorV3Hostname(key))
|
||||
if err == nil {
|
||||
group.Done()
|
||||
t.Errorf("Connection should have failed! %v %v", conn, err)
|
||||
t.Fatalf("Connection should have failed! %v %v", conn, err)
|
||||
}
|
||||
log.Infof("Successfully failed to authenticate...")
|
||||
group.Done()
|
||||
|
|
|
@ -2,9 +2,9 @@ package utils
|
|||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"filippo.io/edwards25519"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// EDH implements diffie hellman using curve25519 keys derived from ed25519 keys
|
||||
|
@ -16,23 +16,68 @@ func EDH(privateKey ed25519.PrivateKey, remotePublicKey ed25519.PublicKey) ([]by
|
|||
var curve25519priv [32]byte
|
||||
|
||||
PrivateKeyToCurve25519(&curve25519priv, &privKeyBytes)
|
||||
remoteCurve25519pub, err := ed25519PublicKeyToCurve25519New(remotePublicKey)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
secret, err := curve25519.X25519(curve25519priv[:], remoteCurve25519pub[:])
|
||||
curve25519pub := ed25519PublicKeyToCurve25519(remotePublicKey)
|
||||
secret, err := curve25519.X25519(curve25519priv[:], curve25519pub[:])
|
||||
return secret, err
|
||||
}
|
||||
|
||||
// reproduced from https://github.com/FiloSottile/age/blob/main/agessh/agessh.go#L190
|
||||
func ed25519PublicKeyToCurve25519New(pk ed25519.PublicKey) ([]byte, error) {
|
||||
// See https://blog.filippo.io/using-ed25519-keys-for-encryption and
|
||||
// https://pkg.go.dev/filippo.io/edwards25519#Point.BytesMontgomery.
|
||||
p, err := new(edwards25519.Point).SetBytes(pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// https://github.com/FiloSottile/age/blob/master/internal/age/ssh.go#L174
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
//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.
|
||||
// * Neither the name of Google LLC nor the names of its
|
||||
//contributors may be used to endorse or promote products derived from
|
||||
//this software without specific prior written permission.
|
||||
//
|
||||
//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
|
||||
//OWNER 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.
|
||||
var curve25519P, _ = new(big.Int).SetString("57896044618658097711785492504343953926634992332820282019728792003956564819949", 10)
|
||||
|
||||
func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte {
|
||||
// ed25519.PublicKey is a little endian representation of the y-coordinate,
|
||||
// with the most significant bit set based on the sign of the x-coordinate.
|
||||
bigEndianY := make([]byte, ed25519.PublicKeySize)
|
||||
for i, b := range pk {
|
||||
bigEndianY[ed25519.PublicKeySize-i-1] = b
|
||||
}
|
||||
return p.BytesMontgomery(), nil
|
||||
bigEndianY[0] &= 0b0111_1111
|
||||
|
||||
// The Montgomery u-coordinate is derived through the bilinear map
|
||||
//
|
||||
// u = (1 + y) / (1 - y)
|
||||
//
|
||||
// See https://blog.filippo.io/using-ed25519-keys-for-encryption.
|
||||
y := new(big.Int).SetBytes(bigEndianY)
|
||||
denom := big.NewInt(1)
|
||||
denom.ModInverse(denom.Sub(denom, y), curve25519P) // 1 / (1 - y)
|
||||
u := y.Mul(y.Add(y, big.NewInt(1)), denom)
|
||||
u.Mod(u, curve25519P)
|
||||
|
||||
out := make([]byte, curve25519.PointSize)
|
||||
uBytes := u.Bytes()
|
||||
for i, b := range uBytes {
|
||||
out[len(uBytes)-i-1] = b
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// PrivateKeyToCurve25519 converts an ed25519 private key into a corresponding
|
||||
|
|
Loading…
Reference in New Issue