Compare commits

...

76 Commits

Author SHA1 Message Date
Sarah Jamie Lewis 4129031391 Error on Non Auth
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is pending Details
2022-10-24 13:10:48 -07:00
Sarah Jamie Lewis 139a35c219 Expose token.T in json + fix issue with new auth protocol 2022-10-24 13:10:48 -07:00
Sarah Jamie Lewis 4e4e3b4422 Formatting + go 1.17 ioutil deprecation 2022-10-24 13:10:48 -07:00
Dan Ballard 015307d907 Merge pull request 'Remove bignum ed25519->curve25519 implementation / replace with filippo.io/edwards25519' (#54) from crypto_improvement into master
continuous-integration/drone/push Build is pending Details
Reviewed-on: #54
2022-09-13 07:00:51 +00:00
Sarah Jamie Lewis 13effd5457 Merge branch 'master' into crypto_improvement
continuous-integration/drone/pr Build is passing Details
2022-09-13 02:15:40 +00:00
Sarah Jamie Lewis 9012720973 Remove bignum ed25519->curve25519 implementation / replace with filippo.io/edwards25519
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is pending Details
2022-09-12 19:13:48 -07:00
Sarah Jamie Lewis af2f509711 Merge pull request 'update drone format' (#53) from drone_update into master
continuous-integration/drone/push Build is passing Details
Reviewed-on: #53
2022-09-06 15:48:49 +00:00
Dan Ballard eceddc676d update drone format
continuous-integration/drone/pr Build is passing Details
2022-09-05 23:28:54 -07:00
Dan Ballard bf7a0a4b39 Merge pull request 'Upgrade Dependencies + Ristretto API' (#52) from update-deps into master
continuous-integration/drone/tag Build was killed Details
continuous-integration/drone/push Build is failing Details
Reviewed-on: #52
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-08-29 03:42:45 +00:00
Sarah Jamie Lewis cefe182b80 Ignore 1 linter error
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-08-28 20:29:29 -07:00
Sarah Jamie Lewis 2da2c98b91 Merge branch 'master' into update-deps
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is passing Details
2022-08-28 20:26:31 -07:00
Sarah Jamie Lewis 7ee88b7ba8 Merge branch 'master' into update-deps
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/pr Build was killed Details
2022-08-28 20:25:39 -07:00
Sarah Jamie Lewis 2da293f118 Upgrade Dependencies
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build was killed Details
2022-08-28 20:22:28 -07:00
Sarah Jamie Lewis ae04cf7983 Merge pull request 'connectivity version bump' (#51) from cbump into master
continuous-integration/drone/push Build is pending Details
Reviewed-on: #51
2022-08-08 20:08:01 +00:00
Dan Ballard 0b5a1a4345 connectivity version bump
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-08-08 12:41:28 -07:00
Dan Ballard f610638146 Merge pull request 'Add Garbage Collection for Old Connections' (#50) from gc into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is pending Details
Reviewed-on: #50
2022-04-21 21:37:36 +00:00
Sarah Jamie Lewis bacc9a47c4 Add Garbage Collection for Old Connections
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-04-21 13:07:42 -07:00
Dan Ballard d21010257d Merge pull request 'Move decrypt error to debug' (#49) from decrypt_error into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is pending Details
Reviewed-on: #49
2022-04-20 23:50:53 +00:00
Sarah Jamie Lewis 29084d0d60 Move decrypt error to debug
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is passing Details
2022-04-20 16:26:04 -07:00
Dan Ballard 10980c7bee Merge pull request 'Reduce new allocations, propagate errors in PRNG.Next()' (#48) from perf into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is pending Details
Reviewed-on: #48
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2022-04-20 19:11:35 +00:00
Sarah Jamie Lewis 89d12f812f Upgrade Connectivity
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-04-20 11:15:11 -07:00
Sarah Jamie Lewis 3b4fee2e72 Reduce new allocations, propagate errors in PRNG.Next()
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-04-19 15:16:51 -07:00
Dan Ballard 4cce393e12 Merge pull request 'Update Connectivity' (#47) from upgrade_conn1.8.2 into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #47
2022-04-18 22:14:21 +00:00
Sarah Jamie Lewis 26141185e0 Merge branch 'master' into upgrade_conn1.8.2
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-04-18 22:05:43 +00:00
Sarah Jamie Lewis f6e1d496c1 Update Connectivity
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is pending Details
2022-04-18 15:04:35 -07:00
erinn 29628df452 Merge pull request 'Expose errors in Send API.' (#46) from send_fixes into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #46
Reviewed-by: erinn <erinn@openprivacy.ca>
2022-01-24 21:58:59 +00:00
Sarah Jamie Lewis 09c0e97336 Merge branch 'master' into send_fixes
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-01-24 20:30:18 +00:00
Sarah Jamie Lewis e87f578c01 Expose errors in Send API.
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is pending Details
2022-01-24 12:28:55 -08:00
erinn 51710f2991 Merge pull request 'Upgrade Connectivity, upgrade quality.sh and drone.yml to use staticcheck' (#45) from upgrade_conn into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is pending Details
Reviewed-on: #45
2022-01-12 20:34:31 +00:00
Sarah Jamie Lewis eac1c48044 Merge branch 'master' into upgrade_conn
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-01-12 20:24:01 +00:00
Sarah Jamie Lewis 100554ebc4 Upgrade Connectivity, upgrade quality.sh and drone.yml to use staticcheck
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-01-12 12:22:47 -08:00
erinn 8655bedf08 Merge pull request 'Upgrade Connectivity' (#44) from upgrades into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #44
Reviewed-by: erinn <erinn@openprivacy.ca>
2021-09-28 21:14:33 +00:00
Sarah Jamie Lewis d80a94b970 Upgrade Connectivity
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-09-28 14:12:57 -07:00
erinn aae071a121 Merge pull request 'Refactor WaitForCapabilityOrClose' (#43) from wait into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is pending Details
Reviewed-on: #43
Reviewed-by: erinn <erinn@openprivacy.ca>
2021-09-27 22:09:35 +00:00
Sarah Jamie Lewis b32cad2c27 Don't Log Conn
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-09-27 14:57:06 -07:00
Sarah Jamie Lewis 12156065c3 Upgrade Logging
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
2021-09-27 14:50:42 -07:00
Sarah Jamie Lewis f1fe281ab4 Fixup debug messages 2021-09-27 14:43:54 -07:00
Sarah Jamie Lewis 2ff23bc89c Merge branch 'master' into wait
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is failing Details
2021-09-27 21:39:26 +00:00
Sarah Jamie Lewis 81810ed531 Refactor WaitForCapabilityOrClose
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is failing Details
2021-09-27 14:38:01 -07:00
erinn 459e00b423 Merge pull request 'Correctly resolve multiple inbound connections to complete-closure' (#42) from networking into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is pending Details
Reviewed-on: #42
Reviewed-by: erinn <erinn@openprivacy.ca>
2021-09-21 23:18:52 +00:00
Sarah Jamie Lewis f1e3f2ca54 Correctly resolve multiple inbound connections to complete-closure
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
Connect() now prevents Open if there are *any connections* under the assumption
that one of them will be resolved valid. (Before connect allowed Open on any
error, this was a bug that would occasionally be tripped by multiple Inbounds)
2021-09-21 15:49:32 -07:00
Dan Ballard 137461e2cd Merge pull request 'Set the App Before Initializing The App Over the Connection...' (#41) from chainbug into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #41
2021-09-10 18:21:59 +00:00
Sarah Jamie Lewis 1750d634f2 Merge branch 'master' into chainbug
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-09-10 18:16:28 +00:00
Sarah Jamie Lewis 759ab44a47 Set the App Before Initializing The App Over the Connection...
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-09-10 11:15:14 -07:00
Dan Ballard 0845156bea Merge pull request 'Actively Deduplicate Connections on WaitForCapabilityOrClose' (#40) from dedupe into master
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is failing Details
Reviewed-on: #40
2021-09-08 20:36:38 +00:00
Sarah Jamie Lewis 364521e4c1 Merge branch 'master' into dedupe
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is passing Details
2021-09-08 18:57:56 +00:00
Sarah Jamie Lewis c19b1011ee Actively Deduplicate Connections on WaitForCapabilityOrClose
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is pending Details
A very rare bug happens when 2 contacts peer with each other at the same
time. This results in duplicate higher level constructs like PeerApp
which can make tracking state-related bugs difficult, especially
in integration tests.

This commit fixes an existing bug in WaitForCapabilityOrClose
which hid the existence of a duplicate connections from clients
(and replaces it with active deduping)
2021-09-08 11:53:39 -07:00
Sarah Jamie Lewis fd31e9c31a Merge pull request 'connecivity version bump' (#39) from conVbump into master
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #39
2021-06-25 09:13:43 -07:00
Dan Ballard d4b9c378eb connecivity version bump
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-06-25 09:12:22 -07:00
Dan Ballard 7f00d05a04 Merge pull request 'Staticcheck and Connectivtiy Upgrade' (#38) from thread_safety into master
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #38
2021-06-09 10:43:09 -07:00
Sarah Jamie Lewis 6e7fcad7a6 Staticcheck and Connectivtiy Upgrade
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-06-09 10:36:34 -07:00
Dan Ballard e7da782cfe Merge pull request 'Fix NPE when ACN is in Error State on Listen' (#37) from bug_fix into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #37
2021-06-02 11:01:52 -07:00
Sarah Jamie Lewis 88ddcc8fa2 Fix NPE when ACN is in Error State on Listen
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-06-02 10:22:39 -07:00
erinn 7f8b475fd7 Merge pull request 'Check solution length in validate challenge' (#36) from bugfix into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #36
2021-05-13 13:01:23 -07:00
Sarah Jamie Lewis 770f36afad Check solution length in validate challenge
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
Found by Fuzz Bot.
2021-05-13 12:38:47 -07:00
erinn 7444d1e0cf Merge pull request 'Don't store server key in token database, make TokenServer responsible for database closure.' (#35) from bugfix into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #35
2021-05-05 12:44:31 -07:00
Sarah Jamie Lewis c47e478e01 Don't store server key in token database, make TokenServer responsible for database closure.
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-05-05 12:03:49 -07:00
Sarah Jamie Lewis 98ff3244c0 Merge pull request 'Upgrade connectivity' (#34) from bugfix into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #34
2021-05-03 14:04:42 -07:00
Sarah Jamie Lewis 38351e486f Upgrade connectivity
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-05-03 14:03:52 -07:00
erinn 0cc499b42c Merge pull request 'Upgrade Connectivity' (#33) from bugfix into master
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #33
2021-04-13 15:10:19 -07:00
Sarah Jamie Lewis e16d2e30f0 Merge branch 'master' into bugfix
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-04-13 15:09:13 -07:00
Sarah Jamie Lewis edba40cfee Upgrade Connectivity
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-04-13 15:08:16 -07:00
Dan Ballard 05d679aa5a Merge pull request 'Upgrade Connectivity' (#32) from bugfix into master
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #32
2021-04-13 13:54:05 -07:00
Sarah Jamie Lewis 1233b1e85a Upgrade Connectivity
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-04-13 13:44:48 -07:00
Dan Ballard cc760fddcb Merge pull request 'Merge missing bugfixes...' (#31) from bugfix into master
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #31
2021-04-09 14:44:58 -07:00
Sarah Jamie Lewis 453a93d579 Merge branch 'hmac_size_fix' into bugfix
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-04-09 14:33:38 -07:00
Sarah Jamie Lewis db89cf80f3 Merge branch 'hmac_size_fix' 2021-04-09 14:18:56 -07:00
Dan Ballard 8f6408433b Merge pull request 'Fix minor "datarace" caused by unecessary assignment after close' (#30) from bugfix into master
Reviewed-on: #30
2021-04-09 14:17:45 -07:00
Dan Ballard 48395506a5 Merge pull request 'Fix minor "datarace" caused by unecessary assignment after close' (#30) from bugfix into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #30
2021-04-09 12:43:00 -07:00
Sarah Jamie Lewis 2310dec631 Fix minor "datarace" caused by unecessary assignment after close
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-04-09 12:29:07 -07:00
Dan Ballard 64317efb0c Merge pull request 'Remove custom url forwarding for tapir module' (#29) from bugfix into master
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #29
2021-04-08 18:17:27 -07:00
Sarah Jamie Lewis b68d838295 Remove custom url forwarding for tapir module
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2021-04-08 18:09:24 -07:00
Sarah Jamie Lewis 905dc52b7a Update Connectivity and Logging 2021-04-08 18:06:08 -07:00
Sarah Jamie Lewis b7a99d6e85 Updating drone for new module name
continuous-integration/drone/push Build is failing Details
2021-04-08 18:05:38 -07:00
Sarah Jamie Lewis 9fba459adc Check if Listener exists before trying to close it 2020-10-29 15:45:07 -07:00
Sarah Jamie Lewis c15ac5767c Add Metrics
continuous-integration/drone/push Build is failing Details
2020-07-14 16:39:08 -07:00
31 changed files with 605 additions and 386 deletions

View File

@ -1,65 +1,73 @@
workspace:
base: /go
path: src/cwtch.im/tapir
---
kind: pipeline
type: docker
name: linux-test
pipeline:
fetch:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
steps:
- name: fetch
image: golang:1.17.5
volumes:
- name: deps
path: /go
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 vendor
- go get -u golang.org/x/lint/golint
quality:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
- go mod download
- go install honnef.co/go/tools/cmd/staticcheck@latest
- name: quality
image: golang:1.17.5
volumes:
- name: deps
path: /go
commands:
- 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
- staticcheck ./...
- name: units-tests
image: golang:1.17.5
volumes:
- name: deps
path: /go
commands:
- export PATH=$PATH:/go/src/cwtch.im/tapir
- export PATH=`pwd`:$PATH
- sh testing/tests.sh
integ-test:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
- name: integ-test
image: golang:1.17.5
volumes:
- name: deps
path: /go
commands:
- export PATH=$PATH:/go/src/cwtch.im/tapir
- go test -race -v cwtch.im/tapir/testing
notify-email:
- export PATH=`pwd`:$PATH
- go test -race -v git.openprivacy.ca/cwtch.im/tapir/testing
- name: notify-email
image: drillster/drone-email
host: build.openprivacy.ca
port: 25
skip_verify: true
from: drone@openprivacy.ca
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
status: [ failure ]
notify-gogs:
- name: notify-gogs
image: openpriv/drone-gogs
pull: if-not-exists
when:
repo: cwtch.im/tapir
branch: master
event: pull_request
status: [ success, changed, failure ]
secrets: [gogs_account_token]
gogs_url: https://git.openprivacy.ca
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

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ coverage.out
*.db
/applications/tokenboard/tor/
fuzzing/
*.cover.out

View File

@ -1,7 +1,7 @@
package tapir
import (
"cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
)
// Capability defines a status granted to a connection, from an application. That allows the connection to access

View File

@ -1,7 +1,7 @@
package applications
import (
"cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir"
)
// ApplicationChain is a meta-app that can be used to build complex applications from other applications
@ -40,13 +40,17 @@ 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 connection.HasCapability(appchain.capabilities[i]) == false {
// if we hit our guard then carry on, otherwise close...
if !connection.HasCapability(appchain.capabilities[i]) {
connection.Close()
return
}
connection.SetApp(app)
}
}

View File

@ -2,9 +2,9 @@ package applications
import (
"crypto/subtle"
"cwtch.im/tapir"
"cwtch.im/tapir/primitives"
"encoding/json"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/primitives"
torProvider "git.openprivacy.ca/openprivacy/connectivity/tor"
"git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/ed25519"
@ -113,7 +113,7 @@ func (ea *AuthApp) Init(connection tapir.Connection) {
connection.SetHostname(assertedHostname)
connection.SetCapability(AuthCapability)
} else {
log.Errorf("Failed Decrypt Challenge: [%x] [%x]\n", remoteChallenge, challengeBytes)
log.Debugf("Failed Decrypt Challenge: [%x] [%x]\n", remoteChallenge, challengeBytes)
connection.Close()
}
}

View File

@ -2,9 +2,9 @@ package applications
import (
"crypto/rand"
"cwtch.im/tapir"
"cwtch.im/tapir/primitives"
"encoding/json"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/primitives"
"golang.org/x/crypto/ed25519"
"testing"
)
@ -17,7 +17,6 @@ type MockConnection struct {
func (mc *MockConnection) Init(outbound bool) {
mc.id, _ = primitives.InitializeEphemeralIdentity()
mc.outbound = outbound
return
}
func (mc MockConnection) Hostname() string {
@ -59,8 +58,9 @@ func (MockConnection) SetEncryptionKey(key [32]byte) {
// no op
}
func (MockConnection) Send(message []byte) {
func (MockConnection) Send(message []byte) error {
// no op
return nil
}
func (MockConnection) Close() {

View File

@ -2,9 +2,10 @@ package applications
import (
"crypto/sha256"
"cwtch.im/tapir"
"cwtch.im/tapir/primitives/core"
"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
@ -66,9 +67,20 @@ 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
}
solution = prng.Next().Encode(nil)
//lint:ignore SA1019 API this is "deprecated", but without it it will cause an allocation on every single check
solution = next.Encode(encodedSolution)
copy(solve[0:], solution[:])
copy(solve[len(solution):], challenge[:])
@ -80,6 +92,8 @@ 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[:]
@ -87,6 +101,9 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
// ValidateChallenge returns true if the message and spamguard pass the challenge
func (powapp *ProofOfWorkApplication) validateChallenge(challenge []byte, solution []byte) bool {
if len(solution) != 32 {
return false
}
solve := make([]byte, len(challenge)+32)
copy(solve[0:], solution[0:32])
copy(solve[32:], challenge[:])

View File

@ -1,9 +1,9 @@
package applications
import (
"cwtch.im/tapir"
"cwtch.im/tapir/primitives/privacypass"
"encoding/json"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
"git.openprivacy.ca/openprivacy/log"
)
@ -55,7 +55,10 @@ func (tokenapp *TokenApplication) Init(connection tapir.Connection) {
var blinded []privacypass.BlindedToken
err := json.Unmarshal(connection.Expect(), &blinded)
if err == nil {
batchProof := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
batchProof, err := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
if err != nil {
return
}
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
data, _ := json.Marshal(batchProof)
connection.Send(data)

View File

@ -1,11 +1,11 @@
package tokenboard
import (
"cwtch.im/tapir"
"cwtch.im/tapir/applications"
"cwtch.im/tapir/primitives/auditable"
"cwtch.im/tapir/primitives/privacypass"
"encoding/json"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/applications"
"git.openprivacy.ca/cwtch.im/tapir/primitives/auditable"
"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
"git.openprivacy.ca/openprivacy/log"
)

View File

@ -1,8 +1,8 @@
package tokenboard
import (
"cwtch.im/tapir/primitives/auditable"
"cwtch.im/tapir/primitives/privacypass"
"git.openprivacy.ca/cwtch.im/tapir/primitives/auditable"
"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
)
// AppHandler allows clients to react to specific events.

View File

@ -3,11 +3,11 @@ package tokenboard
// NOTE: This is a sketch implementation, Not suitable for production use. The real auditable store is still being designed.
import (
"cwtch.im/tapir"
"cwtch.im/tapir/applications"
"cwtch.im/tapir/primitives/auditable"
"cwtch.im/tapir/primitives/privacypass"
"encoding/json"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/applications"
"git.openprivacy.ca/cwtch.im/tapir/primitives/auditable"
"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
"git.openprivacy.ca/openprivacy/log"
)

View File

@ -1,13 +1,12 @@
package tokenboard
import (
"cwtch.im/tapir"
"cwtch.im/tapir/applications"
"cwtch.im/tapir/networks/tor"
"cwtch.im/tapir/primitives"
"cwtch.im/tapir/primitives/auditable"
"cwtch.im/tapir/primitives/privacypass"
"errors"
"git.openprivacy.ca/cwtch.im/tapir/applications"
"git.openprivacy.ca/cwtch.im/tapir/networks/tor"
"git.openprivacy.ca/cwtch.im/tapir/primitives"
"git.openprivacy.ca/cwtch.im/tapir/primitives/auditable"
"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
"git.openprivacy.ca/openprivacy/connectivity"
torProvider "git.openprivacy.ca/openprivacy/connectivity/tor"
"git.openprivacy.ca/openprivacy/log"
@ -39,8 +38,7 @@ type FreePaymentHandler struct {
func (fph *FreePaymentHandler) MakePayment() {
id, sk := primitives.InitializeEphemeralIdentity()
var client tapir.Service
client = new(tor.BaseOnionService)
client := new(tor.BaseOnionService)
client.Init(fph.ACN, sk, &id)
tokenApplication := new(applications.TokenApplication)
@ -75,10 +73,17 @@ func TestTokenBoardApp(t *testing.T) {
log.Infof("Number of goroutines open at start: %d", runtime.NumGoroutine())
os.MkdirAll("./tor/", 0700)
torProvider.GenerateTorrc("tapir-integration-test", "./tor/torrc")
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")
}
// Connect to Tor
acn, err := torProvider.NewTorACNWithAuth("./", "", 9051, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
acn, err := torProvider.NewTorACNWithAuth("./", "", torDataDir, 9060, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
if err != nil {
t.Fatalf("could not launch ACN %v", err)
}
@ -97,8 +102,7 @@ func TestTokenBoardApp(t *testing.T) {
clientAuditableStore.Init(publicsid)
// Init the Server running the Simple App.
var service tapir.Service
service = new(tor.BaseOnionService)
service := new(tor.BaseOnionService)
service.Init(acn, sk, &sid)
// Goroutine Management
@ -110,8 +114,7 @@ func TestTokenBoardApp(t *testing.T) {
}()
// Init the Server running the PoW Token App.
var powTokenService tapir.Service
powTokenService = new(tor.BaseOnionService)
powTokenService := new(tor.BaseOnionService)
spowid, spowk := primitives.InitializeEphemeralIdentity()
powTokenService.Init(acn, spowk, &spowid)
sg.Add(1)
@ -127,8 +130,7 @@ func TestTokenBoardApp(t *testing.T) {
time.Sleep(time.Second * 60) // wait for server to initialize
id, sk := primitives.InitializeEphemeralIdentity()
var client tapir.Service
client = new(tor.BaseOnionService)
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)

View File

@ -1,8 +1,8 @@
package applications
import (
"cwtch.im/tapir"
"cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/log"
)

28
go.mod
View File

@ -1,17 +1,21 @@
module cwtch.im/tapir
module git.openprivacy.ca/cwtch.im/tapir
go 1.17
require (
git.openprivacy.ca/openprivacy/connectivity v1.2.0
git.openprivacy.ca/openprivacy/log v1.0.1
github.com/davecgh/go-spew v1.1.1 // indirect
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.2
github.com/kr/pretty v0.2.0 // indirect
github.com/stretchr/testify v1.4.0 // indirect
go.etcd.io/bbolt v1.3.4
golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
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
)
go 1.13
require (
git.openprivacy.ca/openprivacy/bine v0.0.4 // indirect
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
)

94
go.sum
View File

@ -1,68 +1,56 @@
git.openprivacy.ca/openprivacy/connectivity v1.1.0 h1:9PEeKuPdoIRYeA62BUkBW2BfK4KqKEXz1fvUxZoP4xs=
git.openprivacy.ca/openprivacy/connectivity v1.1.0/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E=
git.openprivacy.ca/openprivacy/connectivity v1.2.0 h1:dbZ5CRl11vg3BNHdzRKSlDP8OUtDB+mf6FkxMVf73qw=
git.openprivacy.ca/openprivacy/connectivity v1.2.0/go.mod h1:B7vzuVmChJtSKoh0ezph5vu6DQ0gIk0zHUNG6IgXCcA=
git.openprivacy.ca/openprivacy/log v1.0.0 h1:Rvqm1weUdR4AOnJ79b1upHCc9vC/QF1rhSD2Um7sr1Y=
git.openprivacy.ca/openprivacy/log v1.0.0/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
git.openprivacy.ca/openprivacy/log v1.0.1 h1:NWV5oBTatvlSzUE6wtB+UQCulgyMOtm4BXGd34evMys=
git.openprivacy.ca/openprivacy/log v1.0.1/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca h1:Q2r7AxHdJwWfLtBZwvW621M3sPqxPc6ITv2j1FGsYpw=
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
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=
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/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
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.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/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/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/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/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a h1:aczoJ0HPNE92XKa7DrIzkNN6esOKO2TBwiiYoKcINhA=
golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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/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-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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/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/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200625195345-7480c7b4547d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=

View File

@ -1,11 +1,12 @@
package tor
import (
"context"
"crypto/rand"
"cwtch.im/tapir"
"cwtch.im/tapir/primitives"
"encoding/base64"
"errors"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/primitives"
"git.openprivacy.ca/openprivacy/connectivity"
"git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/ed25519"
@ -15,12 +16,14 @@ import (
// BaseOnionService is a concrete implementation of the service interface over Tor onion services.
type BaseOnionService struct {
connections sync.Map
acn connectivity.ACN
id *primitives.Identity
privateKey ed25519.PrivateKey
ls connectivity.ListenService
lock sync.Mutex
connections sync.Map
acn connectivity.ACN
id *primitives.Identity
privateKey ed25519.PrivateKey
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
@ -51,27 +54,127 @@ func (s *BaseOnionService) Init(acn connectivity.ACN, sk ed25519.PrivateKey, id
s.acn = acn
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.
func (s *BaseOnionService) SetPort(port int) {
s.port = port
}
// 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) {
conn, err := s.GetConnection(cid)
// 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
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")
}
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 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 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...
if err != nil {
conn, err = s.GetConnection(cid)
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")
}
}
}
// There are no active connections with that hostname
return nil, err
}
// GetConnection returns a connection for a given hostname.
@ -82,11 +185,14 @@ func (s *BaseOnionService) GetConnection(hostname string) (tapir.Connection, err
if connection.Hostname() == hostname {
if !connection.IsClosed() {
conn = append(conn, connection)
return true
} else {
// Delete this Closed Connection
s.connections.Delete(key)
}
}
return true
})
if len(conn) == 0 {
return nil, errors.New("no connection found")
}
@ -98,10 +204,26 @@ 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) {
_, err := s.GetConnection(hostname)
if err == nil {
currconn, _ := s.GetConnection(hostname)
// We already have a connection
if currconn != 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
@ -109,6 +231,7 @@ 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)
@ -119,8 +242,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.
_, err := s.GetConnection(hostname)
if err == nil {
tconn, _ := s.GetConnection(hostname)
if tconn != nil {
conn.Close()
return true, errors.New("already connected to " + hostname)
}
@ -146,12 +269,12 @@ func (s *BaseOnionService) Listen(app tapir.Application) error {
// accepts a new connection
// spins off to a connection struct
s.lock.Lock()
ls, err := s.acn.Listen(s.privateKey, 9878)
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 {
@ -172,12 +295,19 @@ func (s *BaseOnionService) Listen(app tapir.Application) error {
func (s *BaseOnionService) Shutdown() {
s.lock.Lock()
defer s.lock.Unlock()
s.ls.Close()
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

View File

@ -7,8 +7,7 @@ import (
func TestBoltPersistence_Open(t *testing.T) {
os.Remove("test.dbgi")
var db Service
db = new(BoltPersistence)
db := new(BoltPersistence)
db.Open("test.dbgi")
db.Setup([]string{"tokens"})
// 2020.02: Fails in WSL1 because of a mmap issue.

View File

@ -3,11 +3,11 @@ package auditable
// WARNING NOTE: This is a sketch implementation, Not suitable for production use. The real auditable store is still being designed.
import (
"cwtch.im/tapir/persistence"
"cwtch.im/tapir/primitives"
"cwtch.im/tapir/primitives/core"
"encoding/base64"
"errors"
"git.openprivacy.ca/cwtch.im/tapir/persistence"
"git.openprivacy.ca/cwtch.im/tapir/primitives"
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/ed25519"
"sync"
@ -25,7 +25,6 @@ type State struct {
Messages []Message
}
//
const (
auditableDataStoreProtocol = "auditable-data-store"
newMessage = "new-message"
@ -137,7 +136,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) == false {
if !ed25519.Verify(as.identity.PublicKey(), as.LatestCommit, state.SignedProof) {
return errors.New("state is not consistent, the server is malicious")
}
return nil
@ -160,7 +159,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) == false {
if !ed25519.Verify(key, fraudCommit, signedFraudProof) {
// 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

View File

@ -1,9 +1,9 @@
package auditable
import (
"cwtch.im/tapir/persistence"
"cwtch.im/tapir/primitives"
"fmt"
"git.openprivacy.ca/cwtch.im/tapir/persistence"
"git.openprivacy.ca/cwtch.im/tapir/primitives"
"git.openprivacy.ca/openprivacy/log"
"os"
"testing"

View File

@ -2,6 +2,7 @@ package core
import (
"fmt"
"git.openprivacy.ca/openprivacy/log"
"github.com/gtank/merlin"
ristretto "github.com/gtank/ristretto255"
"golang.org/x/crypto/sha3"
@ -11,8 +12,8 @@ import (
// Transcript provides a consistent transcript primitive for our protocols
//
// We have the following goals:
// - Allow sequential proofs over a common transcript (ensuring a single proof cannot be extracted standalone)
// - be able to produce a human-readable transcript for auditing.
// - Allow sequential proofs over a common transcript (ensuring a single proof cannot be extracted standalone)
// - be able to produce a human-readable transcript for auditing.
//
// The design of this API was inspired by Merlin: https://docs.rs/crate/merlin/
type Transcript struct {
@ -38,7 +39,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.Encode([]byte{}))
t.AddToTranscript(label, element.Bytes())
}
// OutputTranscriptToAudit outputs a human-readable copy of the transcript so far.
@ -67,12 +68,14 @@ type PRNG struct {
}
// Next returns the next "random" scalar from the PRNG
func (prng *PRNG) Next() *ristretto.Scalar {
buf := [64]byte{}
io.ReadFull(prng.prng, buf[:])
next := new(ristretto.Scalar)
next.FromUniformBytes(buf[:])
return next
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
}
// CommitToPRNG commits the label to the transcript and derives a PRNG from the transcript.
@ -86,7 +89,8 @@ 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)
return new(ristretto.Element).FromUniformBytes(c)
result, _ := new(ristretto.Element).SetUniformBytes(c)
return result
}
// CommitToGenerators derives a set of verifiably random generators from the transcript
@ -101,6 +105,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.FromUniformBytes(c[:])
s.SetUniformBytes(c[:])
return s
}

View File

@ -11,7 +11,9 @@ func TestNewTranscript(t *testing.T) {
transcript.AddToTranscript("action", []byte("test data"))
if transcript.OutputTranscriptToAudit() != transcript.OutputTranscriptToAudit() {
firstAudit := transcript.OutputTranscriptToAudit()
secondAudit := transcript.OutputTranscriptToAudit()
if firstAudit != secondAudit {
t.Fatalf("Multiple Audit Calls should not impact underlying Transcript")
}
t.Logf("%v", transcript.OutputTranscriptToAudit())

View File

@ -2,7 +2,7 @@ package primitives
import (
"crypto/rand"
"cwtch.im/tapir/utils"
"git.openprivacy.ca/cwtch.im/tapir/utils"
torProvider "git.openprivacy.ca/openprivacy/connectivity/tor"
"golang.org/x/crypto/ed25519"
)

View File

@ -2,12 +2,12 @@ package privacypass
import (
"crypto/rand"
"cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
ristretto "github.com/gtank/ristretto255"
)
// DLEQProof encapsulates a Chaum-Pedersen DLEQ Proof
//gut In Ernest F. Brickell, editor,CRYPTO92,volume 740 ofLNCS, pages 89105. Springer, Heidelberg,August 1993
// gut In Ernest F. Brickell, editor,CRYPTO92,volume 740 ofLNCS, pages 89105. Springer, Heidelberg,August 1993
type DLEQProof struct {
C *ristretto.Scalar
S *ristretto.Scalar
@ -16,26 +16,29 @@ 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))
// s := (t + ck) mod q
//
// A := tX
// B := tP
// c := H(transcript(X,Y,P,Q,A,B))
// s := (t + ck) mod q
//
// Sends c,s to Vicky
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 := new(ristretto.Scalar)
t.FromUniformBytes(private)
t, err := new(ristretto.Scalar).SetUniformBytes(private)
if err != nil {
return DLEQProof{ristretto.NewScalar(), ristretto.NewScalar()}
}
A := new(ristretto.Element).ScalarMult(t, X)
B := new(ristretto.Element).ScalarMult(t, P)
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))
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())
c := transcript.CommitToTranscriptScalar("c")
s := new(ristretto.Scalar).Subtract(t, new(ristretto.Scalar).Multiply(c, k))
@ -45,12 +48,14 @@ 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'))
//
// 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 {
@ -62,12 +67,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.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))
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())
return transcript.CommitToTranscriptScalar("c").Equal(dleq.C) == 1
}

View File

@ -3,8 +3,9 @@ package privacypass
import (
"crypto/hmac"
"crypto/rand"
"cwtch.im/tapir/primitives/core"
"encoding/json"
"fmt"
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/log"
ristretto "github.com/gtank/ristretto255"
@ -55,10 +56,10 @@ func (t *Token) GenBlindedToken() BlindedToken {
t.r = new(ristretto.Scalar)
b := make([]byte, 64)
rand.Read(b)
t.r.FromUniformBytes(b)
t.r.SetUniformBytes(b)
Ht := sha3.Sum512(t.t)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
T, _ := new(ristretto.Element).SetUniformBytes(Ht[:])
P := new(ristretto.Element).ScalarMult(t.r, T)
return BlindedToken{P}
}
@ -70,7 +71,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.Encode(nil)...))
key := sha3.Sum256(append(t.t, t.W.Bytes()...))
mac := hmac.New(sha3.New512, key[:])
mac.Write(data)
return SpentToken{t.t, mac.Sum(nil)}
@ -88,19 +89,25 @@ 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, new(ristretto.Element).Base().Encode(nil))
transcript.AddToTranscript(BatchProofY, Y.Encode(nil))
transcript.AddToTranscript(BatchProofX, ristretto.NewGeneratorElement().Bytes())
transcript.AddToTranscript(BatchProofY, Y.Bytes())
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
prng := transcript.CommitToPRNG("w")
M := new(ristretto.Element).Zero()
Z := new(ristretto.Element).Zero()
M := ristretto.NewIdentityElement()
Z := ristretto.NewIdentityElement()
buf := make([]byte, 64)
c := new(ristretto.Scalar)
for i := range blindedTokens {
c := prng.Next()
err := prng.Next(buf, c)
if err != nil {
log.Errorf("error verifying batch proof: %v", err)
return false
}
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, new(ristretto.Element).Base(), Y, M, Z, transcript)
return VerifyDiscreteLogEquivalenceProof(dleq, ristretto.NewGeneratorElement(), 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
@ -116,3 +123,16 @@ 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,
})
}

View File

@ -1,8 +1,9 @@
package privacypass
import (
"cwtch.im/tapir/persistence"
"cwtch.im/tapir/primitives/core"
"crypto/sha512"
"git.openprivacy.ca/cwtch.im/tapir/persistence"
"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/log"
"github.com/gtank/ristretto255"
"golang.org/x/crypto/sha3"
@ -51,11 +52,16 @@ func TestToken_ConstrainToToken(t *testing.T) {
token2 := new(Token)
blindedToken2 := token2.GenBlindedToken()
Ht := sha3.Sum512(token.t)
T := new(ristretto255.Element).FromUniformBytes(Ht[:])
T, _ := new(ristretto255.Element).SetUniformBytes(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 := server.SignBlindedTokenBatchWithConstraint([]BlindedToken{blindedToken2}, token.t, core.NewTranscript(""))
signedTokens, err := server.SignBlindedTokenBatchWithConstraint([]BlindedToken{blindedToken2}, token.t, core.NewTranscript(""))
if err != nil {
t.Fatalf("error signing tokens with constraints")
}
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.
@ -67,14 +73,20 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
log.SetLevel(log.LevelDebug)
db := new(persistence.BoltPersistence)
db.Open("tokens.db")
defer db.Close()
server := NewTokenServerFromStore(db)
fakeRand := sha512.Sum512([]byte{})
k, _ := ristretto255.NewScalar().SetUniformBytes(fakeRand[:])
server := NewTokenServerFromStore(k, db)
defer server.Close()
clientTranscript := core.NewTranscript("privacyPass")
serverTranscript := core.NewTranscript("privacyPass")
tokens, blindedTokens := GenerateBlindedTokenBatch(10)
batchProof := server.SignBlindedTokenBatch(blindedTokens, serverTranscript)
batchProof, err := server.SignBlindedTokenBatch(blindedTokens, serverTranscript)
if err != nil {
t.Fatalf("error constructing signed/blinded token batch: %v", err)
}
verified := UnblindSignedTokenBatch(tokens, blindedTokens, batchProof.SignedTokens, server.Y, batchProof.Proof, clientTranscript)
@ -98,4 +110,5 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
if verified {
t.Errorf("Something went wrong, the proof passed with wrong transcript: %s", wrongTranscript.OutputTranscriptToAudit())
}
}

View File

@ -3,10 +3,11 @@ package privacypass
import (
"crypto/hmac"
"crypto/rand"
"cwtch.im/tapir/persistence"
"cwtch.im/tapir/primitives/core"
"encoding/hex"
"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"
@ -28,7 +29,6 @@ type SignedBatchWithProof struct {
}
const tokenBucket = "tokens"
const keyBucket = "keys"
// NewTokenServer generates a new TokenServer (used mostly for testing with ephemeral instances)
func NewTokenServer() *TokenServer {
@ -39,34 +39,30 @@ func NewTokenServer() *TokenServer {
// unable to generate secure random numbers
panic("unable to generate secure random numbers")
}
k.FromUniformBytes(b)
k.SetUniformBytes(b)
return &TokenServer{k, new(ristretto.Element).ScalarBaseMult(k), make(map[string]bool), nil, sync.Mutex{}}
}
// NewTokenServerFromStore generates a new TokenServer backed by a persistence service.
func NewTokenServerFromStore(persistenceService persistence.Service) *TokenServer {
func NewTokenServerFromStore(k *ristretto.Scalar, persistenceService persistence.Service) *TokenServer {
tokenServer := NewTokenServer()
persistenceService.Setup([]string{tokenBucket})
persistenceService.Setup([]string{keyBucket})
exists, err := persistenceService.Check(keyBucket, "k")
if err != nil {
panic(err)
}
// if we don't have a stored k then save the one we have generated
// otherwise use the k we have stored
if !exists {
persistenceService.Persist(keyBucket, "k", tokenServer.k)
} else {
persistenceService.Load(keyBucket, "k", tokenServer.k)
// recalculate public key from stored k
tokenServer.Y = new(ristretto.Element).ScalarBaseMult(tokenServer.k)
}
// recalculate public key from k
tokenServer.k = k
tokenServer.Y = new(ristretto.Element).ScalarBaseMult(tokenServer.k)
tokenServer.persistanceService = persistenceService
return tokenServer
}
// Close ensures that the database is properly closed...
func (ts *TokenServer) Close() {
ts.mutex.Lock()
defer ts.mutex.Unlock()
ts.persistanceService.Close()
}
// SignBlindedToken calculates kP for the given BlindedToken P
func (ts *TokenServer) SignBlindedToken(bt BlindedToken) SignedToken {
Q := new(ristretto.Element).ScalarMult(ts.k, bt.P)
@ -74,47 +70,68 @@ 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 {
func (ts *TokenServer) SignBlindedTokenBatch(blindedTokens []BlindedToken, transcript *core.Transcript) (*SignedBatchWithProof, error) {
var signedTokens []SignedToken
for _, bt := range blindedTokens {
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
}
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, signedTokens, transcript)}
proof, err := ts.constructBatchProof(blindedTokens, signedTokens, transcript)
if err != nil {
return nil, err
}
signedProof := SignedBatchWithProof{signedTokens, *proof}
return &signedProof, nil
}
// 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 {
func (ts *TokenServer) SignBlindedTokenBatchWithConstraint(blindedTokens []BlindedToken, constraintToken []byte, transcript *core.Transcript) (*SignedBatchWithProof, error) {
var signedTokens []SignedToken
for _, bt := range blindedTokens {
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
}
Ht := sha3.Sum512(constraintToken)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
T, err := new(ristretto.Element).SetUniformBytes(Ht[:])
if err != nil {
return nil, err
}
// W == kT
W := new(ristretto.Element).ScalarMult(ts.k, T)
blindedTokens = append(blindedTokens, BlindedToken{P: T})
return SignedBatchWithProof{signedTokens, ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: W}), transcript)}
proof, err := ts.constructBatchProof(blindedTokens, append(signedTokens, SignedToken{Q: W}), transcript)
if err != nil {
return nil, err
}
signedProof := SignedBatchWithProof{signedTokens, *proof}
return &signedProof, nil
}
// 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 {
func (ts *TokenServer) constructBatchProof(blindedTokens []BlindedToken, signedTokens []SignedToken, transcript *core.Transcript) (*DLEQProof, error) {
transcript.NewProtocol(BatchProofProtocol)
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
transcript.AddToTranscript(BatchProofY, ts.Y.Encode(nil))
transcript.AddToTranscript(BatchProofX, ristretto.NewGeneratorElement().Bytes())
transcript.AddToTranscript(BatchProofY, ts.Y.Bytes())
transcript.AddToTranscript(BatchProofPVector, []byte(fmt.Sprintf("%v", blindedTokens)))
transcript.AddToTranscript(BatchProofQVector, []byte(fmt.Sprintf("%v", signedTokens)))
prng := transcript.CommitToPRNG("w")
M := new(ristretto.Element).Zero()
Z := new(ristretto.Element).Zero()
M := ristretto.NewIdentityElement()
Z := ristretto.NewIdentityElement()
buf := make([]byte, 64)
c := new(ristretto.Scalar)
for i := range blindedTokens {
c := prng.Next()
err := prng.Next(buf, c)
if err != nil {
log.Errorf("error constructing batch proof: %v", err)
return nil, err
}
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 DiscreteLogEquivalenceProof(ts.k, new(ristretto.Element).Base(), ts.Y, M, Z, transcript)
proof := DiscreteLogEquivalenceProof(ts.k, ristretto.NewGeneratorElement(), ts.Y, M, Z, transcript)
return &proof, nil
}
// SpendToken returns true a SpentToken is valid and has never been spent before, false otherwise.
@ -127,19 +144,22 @@ func (ts *TokenServer) SpendToken(token SpentToken, data []byte) error {
}
} else {
spent, err := ts.persistanceService.Check(tokenBucket, hex.EncodeToString(token.T))
if err != nil || spent == true {
if err != nil || spent {
return fmt.Errorf("token: %v has already been spent", token)
}
}
Ht := sha3.Sum512(token.T)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
T, err := new(ristretto.Element).SetUniformBytes(Ht[:])
if err != nil {
return err
}
W := new(ristretto.Element).ScalarMult(ts.k, T)
key := sha3.Sum256(append(token.T, W.Encode(nil)...))
key := sha3.Sum256(append(token.T, W.Bytes()...))
mac := hmac.New(sha3.New512, key[:])
mac.Write(data)
computedMAC := mac.Sum(nil)
result := hmac.Equal(token.MAC, computedMAC)
if result == true {
if result {
if ts.persistanceService == nil {
ts.seen[hex.EncodeToString(token.T)] = true
} else {

View File

@ -2,8 +2,9 @@ package tapir
import (
"crypto/rand"
"cwtch.im/tapir/primitives"
"encoding/binary"
"errors"
"git.openprivacy.ca/cwtch.im/tapir/primitives"
"git.openprivacy.ca/openprivacy/connectivity"
"git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/ed25519"
@ -39,7 +40,7 @@ type Connection interface {
HasCapability(name Capability) bool
SetCapability(name Capability)
SetEncryptionKey(key [32]byte)
Send(message []byte)
Send(message []byte) error
Close()
App() Application
SetApp(application Application)
@ -61,6 +62,7 @@ type connection struct {
MaxLength int
lock sync.Mutex
service Service
expectBuffer []byte
}
// NewConnection creates a new Connection
@ -73,6 +75,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
}
@ -141,42 +144,45 @@ func (c *connection) HasCapability(name Capability) bool {
func (c *connection) Close() {
c.lock.Lock()
defer c.lock.Unlock()
c.closeInner()
}
func (c *connection) closeInner() {
c.closed = true
c.conn.Close()
}
// Expect blocks and reads a single Tapir packet , from the connection.
func (c *connection) Expect() []byte {
buffer := make([]byte, c.MaxLength)
n, err := io.ReadFull(c.conn, buffer)
// 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)
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)
c.conn.Close()
c.closed = true
c.Close() // use the full close function which acquires a lock for the connection state...
return []byte{}
}
c.lock.Lock()
defer c.lock.Unlock()
if c.encrypted {
var decryptNonce [24]byte
copy(decryptNonce[:], buffer[:24])
decrypted, ok := secretbox.Open(nil, buffer[24:], &decryptNonce, &c.key)
copy(decryptNonce[:], c.expectBuffer[:24])
decrypted, ok := secretbox.Open(nil, c.expectBuffer[24:], &decryptNonce, &c.key)
if ok {
copy(buffer, decrypted)
copy(c.expectBuffer, decrypted)
} else {
log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.hostname, c.identity.Hostname())
c.conn.Close()
c.closed = true
c.closeInner()
return []byte{}
}
}
length, _ := binary.Uvarint(buffer[0:2])
length, _ := binary.Uvarint(c.expectBuffer[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 buffer[2 : length+2]
return c.expectBuffer[2 : length+2]
}
// SetEncryptionKey turns on application-level encryption on the connection using the given key.
@ -188,7 +194,13 @@ 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) {
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")
}
buffer := make([]byte, c.MaxLength)
binary.PutUvarint(buffer[0:2], uint64(len(message)))
@ -200,8 +212,8 @@ func (c *connection) Send(message []byte) {
var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
log.Errorf("Could not read sufficient randomness %v. Closing connection", err)
c.conn.Close()
c.closed = true
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)
@ -210,9 +222,9 @@ func (c *connection) Send(message []byte) {
log.Debugf("[%v -> %v] Wire Send %x", c.identity.Hostname(), c.hostname, buffer)
_, err := c.conn.Write(buffer)
if err != nil {
c.conn.Close()
c.closed = true
c.closeInner()
}
return err
}
// Broadcast sends a message to all active service connections with a given capability

View File

@ -9,7 +9,7 @@ go list ./... | xargs go vet
echo ""
echo "Linting:"
go list ./... | xargs golint
staticcheck ./...
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 "vendor/" | grep -v "go.sum" | grep -v ".idea"
misspell . | grep -v "testing/" | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"

View File

@ -1,16 +1,17 @@
package testing
import (
"cwtch.im/tapir"
"cwtch.im/tapir/applications"
"cwtch.im/tapir/networks/tor"
"cwtch.im/tapir/primitives"
"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"
"git.openprivacy.ca/openprivacy/connectivity"
torProvider "git.openprivacy.ca/openprivacy/connectivity/tor"
"git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/ed25519"
"os"
"runtime"
"runtime/pprof"
"sync"
"testing"
"time"
@ -62,10 +63,17 @@ func TestTapir(t *testing.T) {
log.Infof("Number of goroutines open at start: %d", runtime.NumGoroutine())
// Connect to Tor
os.MkdirAll("./tor/", 0700)
torProvider.GenerateTorrc("tapir-integration-test", "./tor/torrc")
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")
}
// Connect to Tor
acn, err := torProvider.NewTorACNWithAuth("./", "", 9051, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
acn, err := torProvider.NewTorACNWithAuth("./", "", torDataDir, 9060, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
if err != nil {
t.Fatalf("could not launch ACN %v", err)
}
@ -75,8 +83,7 @@ func TestTapir(t *testing.T) {
id, sk := primitives.InitializeEphemeralIdentity()
// Init the Server running the Simple App.
var service tapir.Service
service = new(tor.BaseOnionService)
service := new(tor.BaseOnionService)
service.Init(acn, sk, &id)
// Goroutine Management
@ -93,17 +100,22 @@ func TestTapir(t *testing.T) {
wg.Add(2)
// Init a Client to Connect to the Server
client, clienthostname := genclient(acn)
go connectclient(client, id.PublicKey(), wg)
go connectclient(t, client, id.PublicKey(), wg)
CheckConnection(service, clienthostname, wg)
wg.Wait()
// Wait for Server to Sync
time.Sleep(time.Second * 2)
// Wait for Garbage Collection...
time.Sleep(time.Second * 60)
log.Infof("Closing ACN...")
client.Shutdown()
service.Shutdown()
acn.Close()
sg.Wait()
time.Sleep(time.Second * 2)
time.Sleep(time.Second * 5)
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 {
@ -114,14 +126,13 @@ func TestTapir(t *testing.T) {
func genclient(acn connectivity.ACN) (tapir.Service, string) {
id, sk := primitives.InitializeEphemeralIdentity()
var client tapir.Service
client = new(tor.BaseOnionService)
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(client tapir.Service, key ed25519.PublicKey, group *sync.WaitGroup) {
func connectclient(t *testing.T, 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
@ -130,6 +141,18 @@ func connectclient(client tapir.Service, key ed25519.PublicKey, group *sync.Wait
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()
}

View File

@ -1,10 +1,10 @@
package testing
import (
"cwtch.im/tapir"
"cwtch.im/tapir/applications"
"cwtch.im/tapir/networks/tor"
"cwtch.im/tapir/primitives"
"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"
torProvider "git.openprivacy.ca/openprivacy/connectivity/tor"
"git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/ed25519"
@ -22,10 +22,19 @@ func TestTapirMaliciousRemote(t *testing.T) {
log.Infof("Number of goroutines open at start: %d", runtime.NumGoroutine())
// Connect to Tor
os.MkdirAll("./tor/", 0700)
torProvider.GenerateTorrc("tapir-integration-test", "./tor/torrc")
builder := new(torProvider.TorrcBuilder)
builder.WithHashedPassword("tapir-integration-test").Build("./tor/torrc")
// Connect to Tor
acn, err := torProvider.NewTorACNWithAuth("./", "", 9051, torProvider.HashedPasswordAuthenticator{Password: "tapir-integration-test"})
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"})
if err != nil {
t.Fatalf("could not launch ACN %v", err)
}
@ -36,8 +45,7 @@ func TestTapirMaliciousRemote(t *testing.T) {
id2, sk2 := primitives.InitializeEphemeralIdentity()
// Init the Server running the Simple App.
var service tapir.Service
service = new(tor.BaseOnionService)
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)
@ -62,6 +70,8 @@ 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...
@ -83,7 +93,7 @@ func connectclientandfail(client tapir.Service, key ed25519.PublicKey, group *sy
conn, err := client.GetConnection(torProvider.GetTorV3Hostname(key))
if err == nil {
group.Done()
t.Fatalf("Connection should have failed! %v %v", conn, err)
t.Errorf("Connection should have failed! %v %v", conn, err)
}
log.Infof("Successfully failed to authenticate...")
group.Done()

View File

@ -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,68 +16,23 @@ func EDH(privateKey ed25519.PrivateKey, remotePublicKey ed25519.PublicKey) ([]by
var curve25519priv [32]byte
PrivateKeyToCurve25519(&curve25519priv, &privKeyBytes)
curve25519pub := ed25519PublicKeyToCurve25519(remotePublicKey)
secret, err := curve25519.X25519(curve25519priv[:], curve25519pub[:])
remoteCurve25519pub, err := ed25519PublicKeyToCurve25519New(remotePublicKey)
if err != nil {
return []byte{}, err
}
secret, err := curve25519.X25519(curve25519priv[:], remoteCurve25519pub[:])
return secret, 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
// 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
}
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
return p.BytesMontgomery(), nil
}
// PrivateKeyToCurve25519 converts an ed25519 private key into a corresponding