Compare commits

..

3 Commits

23 changed files with 309 additions and 328 deletions

View File

@ -4,10 +4,6 @@ workspace:
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
@ -17,33 +13,21 @@ pipeline:
- 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:
- 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=$PATH:/go/src/cwtch.im/tapir
- sh testing/tests.sh
integ-test:
when:
repo: cwtch.im/tapir
branch: master
event: [ push, pull_request ]
image: golang
commands:
- ./tor -f ./torrc
- sleep 15
- go test -race -v cwtch.im/tapir/testing
- go test -v cwtch.im/tapir/testing
notify-email:
image: drillster/drone-email
host: build.openprivacy.ca
@ -51,15 +35,10 @@ pipeline:
skip_verify: true
from: drone@openprivacy.ca
when:
repo: cwtch.im/tapir
branch: master
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]

2
.gitignore vendored
View File

@ -5,5 +5,3 @@ coverage.out
/testing/tor/
/applications/tor/
*.db
/applications/tokenboard/tor/
fuzzing/

View File

@ -63,7 +63,7 @@ func (ea *AuthApp) Init(connection tapir.Connection) {
challengeRemote, _ := json.Marshal(remoteAuthMessage)
challengeLocal, _ := json.Marshal(authMessage)
// Define canonical labels so both sides of the connection can generate the same key
// Define canonical labels so both sides of the
var outboundAuthMessage []byte
var outboundHostname string
var inboundAuthMessage []byte
@ -96,16 +96,14 @@ func (ea *AuthApp) Init(connection tapir.Connection) {
// Since we have set the encryption key on the connection the connection will encrypt any messages we send with that key
// To test that the remote peer has done the same we calculate a challenge hash based on the transcript so far and send it to them
// along with our hostname
// We expect the remote to do the same, and compare the two.
// If successful we extend our auth capability to the connection and reassert the hostname.
// We note that the only successful scenario here requires that the remote peer have successfully derived the same
// encryption key and the same transcript challenge.
connection.Send(append(challengeBytes, []byte(connection.ID().Hostname())...))
connection.Send(challengeBytes)
remoteChallenge := connection.Expect()
assertedHostname := utils.GetTorV3Hostname(remoteAuthMessage.LongTermPublicKey)
if subtle.ConstantTimeCompare(append(challengeBytes, []byte(assertedHostname)...), remoteChallenge) == 1 {
connection.SetHostname(assertedHostname)
if subtle.ConstantTimeCompare(challengeBytes, remoteChallenge) == 1 {
connection.SetHostname(utils.GetTorV3Hostname(remoteAuthMessage.LongTermPublicKey))
connection.SetCapability(AuthCapability)
} else {
log.Errorf("Failed Decrypt Challenge: [%x] [%x]\n", remoteChallenge, challengeBytes)

View File

@ -68,7 +68,7 @@ func (powapp *ProofOfWorkApplication) solveChallenge(challenge []byte, prng core
solve := make([]byte, len(challenge)+32)
for !solved {
solution = prng.Next().Encode(nil)
solution = prng.Next().Bytes()
copy(solve[0:], solution[:])
copy(solve[len(solution):], challenge[:])

View File

@ -16,49 +16,41 @@ type TokenApplication struct {
// HasTokensCapability is granted once the client has obtained signed tokens
const HasTokensCapability = tapir.Capability("HasTokensCapability")
const numTokens = 10
// NewInstance should always return a new instantiation of the application.
func (tokenapp *TokenApplication) NewInstance() tapir.Application {
func (powapp *TokenApplication) NewInstance() tapir.Application {
app := new(TokenApplication)
app.TokenService = tokenapp.TokenService
app.TokenService = powapp.TokenService
return app
}
// Init is run when the connection is first started.
func (tokenapp *TokenApplication) Init(connection tapir.Connection) {
tokenapp.Transcript().NewProtocol("token-app")
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
func (powapp *TokenApplication) Init(connection tapir.Connection) {
powapp.Transcript().NewProtocol("token-app")
if connection.IsOutbound() {
tokens, blinded := privacypass.GenerateBlindedTokenBatch(numTokens)
tokens, blinded := privacypass.GenerateBlindedTokenBatch(10)
data, _ := json.Marshal(blinded)
connection.Send(data)
var signedBatch privacypass.SignedBatchWithProof
err := json.Unmarshal(connection.Expect(), &signedBatch)
if err == nil {
verified := privacypass.UnblindSignedTokenBatch(tokens, blinded, signedBatch.SignedTokens, tokenapp.TokenService.Y, signedBatch.Proof, tokenapp.Transcript())
verified := privacypass.UnblindSignedTokenBatch(tokens, blinded, signedBatch.SignedTokens, powapp.TokenService.Y, signedBatch.Proof, powapp.Transcript())
if verified {
log.Debugf("Successfully obtained signed tokens")
tokenapp.Tokens = tokens
powapp.Tokens = tokens
connection.SetCapability(HasTokensCapability)
return
}
// This will close the connection by default and no tokens will be available.
// This usecase can be checked by the existing WaitForCapabilityOrClose() function using the HasTokensCapability
// If the connection closes without the HasTokensCapability then the error can be handled by whatever client needs it
log.Debugf("Failed to verify signed token batch")
}
return
}
// We are the server
var blinded []privacypass.BlindedToken
err := json.Unmarshal(connection.Expect(), &blinded)
if err == nil {
batchProof := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
data, _ := json.Marshal(batchProof)
connection.Send(data)
return
} else {
var blinded []privacypass.BlindedToken
err := json.Unmarshal(connection.Expect(), &blinded)
if err == nil {
batchProof := powapp.TokenService.SignBlindedTokenBatch(blinded, powapp.Transcript())
data, _ := json.Marshal(batchProof)
connection.Send(data)
return
}
}
}

View File

@ -60,6 +60,7 @@ func (ta *Client) Listen() {
var message Message
json.Unmarshal(data, &message)
log.Debugf("Received a Message: %v", message)
switch message.MessageType {
case postResultMessage:
log.Debugf("Post result: %x", message.PostResult.Proof)
@ -100,7 +101,7 @@ func (ta *Client) PurchaseTokens() {
// Post sends a Post Request to the server
func (ta *Client) Post(message auditable.Message) bool {
token, err := ta.paymentHandler.NextToken(message, ta.connection.Hostname())
token, err := ta.paymentHandler.NextToken(message)
if err == nil {
data, _ := json.Marshal(Message{MessageType: postRequestMessage, PostRequest: postRequest{Token: token, Message: message}})
ta.connection.Send(data)

View File

@ -1,7 +1,5 @@
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"
@ -57,7 +55,7 @@ func (ta *Server) Listen() {
var message Message
json.Unmarshal(data, &message)
log.Debugf("Received a Message: %v", message)
switch message.MessageType {
case postRequestMessage:
postrequest := message.PostRequest
@ -79,7 +77,7 @@ func (ta *Server) Listen() {
}
func (ta *Server) postMessageRequest(token privacypass.SpentToken, message auditable.Message) {
if err := ta.TokenService.SpendToken(token, append(message, ta.connection.ID().Hostname()...)); err == nil {
if err := ta.TokenService.SpendToken(token, message); err == nil {
log.Debugf("Token is valid")
signedproof := ta.AuditableStore.Add(message)
data, _ := json.Marshal(Message{MessageType: postResultMessage, PostResult: postResult{true, signedproof}})

View File

@ -47,24 +47,21 @@ func (fph *FreePaymentHandler) MakePayment() {
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
ChainApplication(tokenApplication, applications.HasTokensCapability)
client.Connect(fph.ServerHostname, powTokenApp)
conn, err := client.WaitForCapabilityOrClose(fph.ServerHostname, applications.HasTokensCapability)
if err == nil {
powtapp, _ := conn.App().(*applications.TokenApplication)
fph.tokens = append(fph.tokens, powtapp.Tokens...)
log.Debugf("Transcript: %v", powtapp.Transcript().OutputTranscriptToAudit())
conn.Close()
return
}
log.Debugf("Error making payment: %v", err)
client.WaitForCapabilityOrClose(fph.ServerHostname, applications.HasTokensCapability)
conn, _ := client.GetConnection(fph.ServerHostname)
powtapp, _ := conn.App().(*applications.TokenApplication)
fph.tokens = append(fph.tokens, powtapp.Tokens...)
log.Debugf("Transcript: %v", powtapp.Transcript().OutputTranscriptToAudit())
conn.Close()
}
func (fph *FreePaymentHandler) NextToken(data []byte, hostname string) (privacypass.SpentToken, error) {
func (fph *FreePaymentHandler) NextToken(data []byte) (privacypass.SpentToken, error) {
if len(fph.tokens) == 0 {
return privacypass.SpentToken{}, errors.New("No more tokens")
}
token := fph.tokens[0]
fph.tokens = fph.tokens[1:]
return token.SpendToken(append(data, hostname...)), nil
return token.SpendToken(data), nil
}
func TestTokenBoardApp(t *testing.T) {
@ -97,7 +94,7 @@ func TestTokenBoardApp(t *testing.T) {
sg := new(sync.WaitGroup)
sg.Add(1)
go func() {
service.Listen(NewTokenBoardServer(tokenService, serverAuditableStore))
service.Listen(NewTokenBoardServer(&tokenService, serverAuditableStore))
sg.Done()
}()
@ -109,7 +106,7 @@ func TestTokenBoardApp(t *testing.T) {
sg.Add(1)
go func() {
tokenApplication := new(applications.TokenApplication)
tokenApplication.TokenService = tokenService
tokenApplication.TokenService = &tokenService
powTokenApp := new(applications.ApplicationChain).
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
ChainApplication(tokenApplication, applications.HasTokensCapability)
@ -117,12 +114,12 @@ func TestTokenBoardApp(t *testing.T) {
sg.Done()
}()
time.Sleep(time.Second * 60) // wait for server to initialize
time.Sleep(time.Second * 30) // wait for server to initialize
id, sk := primitives.InitializeEphemeralIdentity()
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.Connect(sid.Hostname(), NewTokenBoardClient(clientAuditableStore, Handler{Store: clientAuditableStore}, &FreePaymentHandler{ACN: acn, TokenService: &tokenService, ServerHostname: spowid.Hostname()}))
client.WaitForCapabilityOrClose(sid.Hostname(), applications.AuthCapability)
conn, _ := client.GetConnection(sid.Hostname())
tba, _ := conn.App().(*Client)

6
go.mod
View File

@ -2,11 +2,5 @@ module cwtch.im/tapir
require (
git.openprivacy.ca/openprivacy/libricochet-go v1.0.4
github.com/gtank/merlin v0.1.1
github.com/gtank/ristretto255 v0.1.2
go.etcd.io/bbolt v1.3.3
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
)
go 1.13

11
go.sum
View File

@ -8,19 +8,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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/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/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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
@ -28,9 +20,8 @@ golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -54,6 +54,7 @@ func (s *BaseOnionService) WaitForCapabilityOrClose(cid string, name tapir.Capab
func (s *BaseOnionService) GetConnection(hostname string) (tapir.Connection, error) {
var conn tapir.Connection
s.connections.Range(func(key, value interface{}) bool {
log.Debugf("Checking %v", key)
connection := value.(tapir.Connection)
if connection.Hostname() == hostname {
if !connection.IsClosed() {

View File

@ -53,7 +53,6 @@ func (bp *BoltPersistence) Check(bucket string, name string) (bool, error) {
val = b.Get([]byte(name))
return nil
})
if err != nil {
return false, err
} else if val != nil {

View File

@ -1,7 +1,5 @@
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"

62
primitives/bloom.go Normal file
View File

@ -0,0 +1,62 @@
package primitives
import (
"crypto/sha256"
"math"
"math/big"
"sync"
)
// BloomFilter implements a bloom filter
type BloomFilter struct {
B []bool
lock sync.Mutex
}
// Init constructs a bloom filter of size m
func (bf *BloomFilter) Init(m int64) {
bf.B = make([]bool, m)
}
// Hash transforms a message to a set of bit flips
func (bf *BloomFilter) Hash(msg []byte) []int {
// Not the fastest hash function ever, but cryptographic security is more important than speed.
hash1 := sha256.Sum256(append([]byte("h1"), msg...))
hash2 := sha256.Sum256(append([]byte("h2"), msg...))
hash3 := sha256.Sum256(append([]byte("h3"), msg...))
hash4 := sha256.Sum256(append([]byte("h4"), msg...))
m := int64(len(bf.B))
// Number of bytes needed to pick a position from [0,m)
B := int(math.Ceil(math.Log2(float64(m)) / 8.0))
p1 := big.NewInt(0).SetBytes(hash1[:B]).Int64()
p2 := big.NewInt(0).SetBytes(hash2[:B]).Int64()
p3 := big.NewInt(0).SetBytes(hash3[:B]).Int64()
p4 := big.NewInt(0).SetBytes(hash4[:B]).Int64()
return []int{int(p1), int(p2), int(p3), int(p4)}
}
// Insert updates the BloomFilter (suitable for concurrent use)
func (bf *BloomFilter) Insert(msg []byte) {
pos := bf.Hash(msg)
bf.lock.Lock()
defer bf.lock.Unlock()
bf.B[pos[0]] = true
bf.B[pos[1]] = true
bf.B[pos[2]] = true
bf.B[pos[3]] = true
}
// Check returns true if the messages might be in the BloomFilter
// (No false positives, possible false negatives due to the probabilistic nature of the filter)
func (bf *BloomFilter) Check(msg []byte) bool {
pos := bf.Hash(msg)
if bf.B[pos[0]] && bf.B[pos[1]] && bf.B[pos[2]] && bf.B[pos[3]] {
return true
}
return false
}

24
primitives/bloom_test.go Normal file
View File

@ -0,0 +1,24 @@
package primitives
import (
"strconv"
"testing"
)
func TestBloomFilter_Insert(t *testing.T) {
bf := new(BloomFilter)
bf.Init(256)
fp := 0
for i := 0; i < 256; i++ {
input := []byte("test" + strconv.Itoa(256+i))
if bf.Check(input) {
t.Log("False Positive!")
fp++
}
bf.Insert(input)
}
t.Logf("Num false positives %v %v%%", fp, (float64(fp)/256.0)*100)
}

View File

@ -2,28 +2,32 @@ package core
import (
"fmt"
"github.com/gtank/merlin"
ristretto "github.com/gtank/ristretto255"
"github.com/bwesterb/go-ristretto"
"golang.org/x/crypto/sha3"
"hash"
"io"
)
// Transcript provides a consistent transcript primitive for our protocols
// Transcript implements a transcript of a public coin argument.
//
// We have the following goals:
// - Provide a consisted transcript API for our zero knowledge protocols
// - 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.
// - produce an auditable human-readable transcript.
//
// The design of this API was inspired by Merlin: https://docs.rs/crate/merlin/
//
// At some point we might want to extend this to be compatible with Merlin transcripts, built on STROBE
type Transcript struct {
merlinTranscript *merlin.Transcript
transcript string
hash hash.Hash
transcript string
}
// NewTranscript creates a new Transcript with the given Label, the label should be unique to the application
func NewTranscript(label string) *Transcript {
transcript := new(Transcript)
transcript.merlinTranscript = merlin.NewTranscript(label)
transcript.hash = sha3.New256()
transcript.AddToTranscript("protocol", []byte(label))
return transcript
}
@ -32,13 +36,7 @@ func NewTranscript(label string) *Transcript {
func (t *Transcript) AddToTranscript(label string, b []byte) {
op := fmt.Sprintf("%s (%d) %x;", label, len(b), b)
t.transcript = fmt.Sprintf("%v\n%v", t.transcript, op)
t.merlinTranscript.AppendMessage([]byte(label), b)
}
// 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.hash.Write([]byte(op))
}
// OutputTranscriptToAudit outputs a human-readable copy of the transcript so far.
@ -51,13 +49,14 @@ func (t Transcript) OutputTranscriptToAudit() string {
func (t *Transcript) NewProtocol(label string) {
op := fmt.Sprintf("---- new-protcol: %s ----", label)
t.transcript = fmt.Sprintf("%v\n%v", t.transcript, op)
t.merlinTranscript.AppendMessage([]byte("protocol"), []byte(label))
t.hash.Write([]byte(op))
}
// CommitToTranscript generates a challenge based on the current transcript, it also commits the challenge to the transcript.
func (t *Transcript) CommitToTranscript(label string) []byte {
b := t.merlinTranscript.ExtractBytes([]byte(label), 64)
t.transcript = fmt.Sprintf("%v\nextract %v: %v", t.transcript, label, b)
t.AddToTranscript("commit", []byte(label))
b := t.hash.Sum([]byte{})
t.AddToTranscript(label, b)
return b
}
@ -68,39 +67,25 @@ type PRNG struct {
// Next returns the next "random" scalar from the PRNG
func (prng *PRNG) Next() *ristretto.Scalar {
buf := [64]byte{}
buf := [32]byte{}
io.ReadFull(prng.prng, buf[:])
next := new(ristretto.Scalar)
next.FromUniformBytes(buf[:])
return next
return new(ristretto.Scalar).SetBytes(&buf)
}
// CommitToPRNG commits the label to the transcript and derives a PRNG from the transcript.
func (t *Transcript) CommitToPRNG(label string) PRNG {
b := t.merlinTranscript.ExtractBytes([]byte(label), 64)
t.AddToTranscript("commit-prng", []byte(label))
b := t.hash.Sum([]byte{})
t.AddToTranscript(label, b)
prng := sha3.NewShake256()
prng.Write(b)
return PRNG{prng: 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)
}
// CommitToGenerators derives a set of verifiably random generators from the transcript
func (t *Transcript) CommitToGenerators(label string, n int) (generators []*ristretto.Element) {
for i := 0; i < n; i++ {
generators = append(generators, t.CommitToGenerator(fmt.Sprintf("%v-%d", label, i)))
}
return generators
}
// CommitToTranscriptScalar is a convenience method for CommitToTranscript which returns a ristretto Scalar
func (t *Transcript) CommitToTranscriptScalar(label string) *ristretto.Scalar {
c := t.CommitToTranscript(label)
s := new(ristretto.Scalar)
s.FromUniformBytes(c[:])
return s
cs := [32]byte{}
copy(cs[:], c[:])
return new(ristretto.Scalar).SetBytes(&cs)
}

71
primitives/dlog.go Normal file
View File

@ -0,0 +1,71 @@
package primitives
import (
"cwtch.im/tapir/primitives/core"
"github.com/bwesterb/go-ristretto"
)
// DLProof Encapsulates a Discrete Log / Schnorr Proof
// Note that these parameters are read-only.
type DLProof struct {
V, A ristretto.Point
R ristretto.Scalar
}
// DiscreteLogProof - Proof of Knowledge of Exponent
// Given V = xG
// Peggy: z := choose randomly from Zq
// A := zG
// c := H(transcript(G,V,A)) mod q
// r := (z + cx) mod q
//
// Sends A,r,V to Vicky
func DiscreteLogProof(x ristretto.Scalar, v ristretto.Point, transcript *core.Transcript) (proof DLProof) {
transcript.AddToTranscript("G", new(ristretto.Point).SetBase().Bytes())
// We bind the proof to our public V
proof.V = v
transcript.AddToTranscript("V", proof.V.Bytes())
// Generate a random z
// A := zG
z := new(ristretto.Scalar).Rand()
proof.A = *new(ristretto.Point).ScalarMultBase(z)
transcript.AddToTranscript("A", proof.A.Bytes())
// Derive Challenge
c := transcript.CommitToTranscriptScalar("c")
// r := (z + cx) mod p
cx := new(ristretto.Scalar).Mul(c, &x)
proof.R = *new(ristretto.Scalar).Add(z, cx)
return
}
// VerifyDiscreteLogProof validates a given Schnorr Proof
// Vicky gets A,r,V from Peggy
// Vicky computes c := H(transcript(G,V,A)) mod q
// Vicky checks rG := A + cV
// rG ?= zG + cV
// (z+cx)G ?= zG + cV
// ?= zG + cxG
// Thus demonstrating that Peggy knows the discrete log to V
func VerifyDiscreteLogProof(proof DLProof, transcript *core.Transcript) bool {
transcript.AddToTranscript("G", new(ristretto.Point).SetBase().Bytes())
transcript.AddToTranscript("V", proof.V.Bytes())
transcript.AddToTranscript("A", proof.A.Bytes())
c := transcript.CommitToTranscriptScalar("c")
// Compute left hand side
lhs := new(ristretto.Point).ScalarMultBase(&proof.R)
// Compute right hand side
cV := new(ristretto.Point).ScalarMult(&proof.V, c)
rhs := new(ristretto.Point).Add(&proof.A, cV)
// Result of verification: lhs ?= rhs
return lhs.Equals(rhs)
}

View File

@ -1,20 +1,19 @@
package privacypass
import (
"crypto/rand"
"cwtch.im/tapir/primitives/core"
ristretto "github.com/gtank/ristretto255"
"github.com/bwesterb/go-ristretto"
)
// DLEQProof encapsulates a Chaum-Pedersen DLEQ Proof
//gut In Ernest F. Brickell, editor,CRYPTO92,volume 740 ofLNCS, pages 89105. Springer, Heidelberg,August 1993
// David Chaum and Torben P. Pedersen. Wallet databaseswith observers. In Ernest F. Brickell, editor,CRYPTO92,volume 740 ofLNCS, pages 89105. Springer, Heidelberg,August 1993
type DLEQProof struct {
C *ristretto.Scalar
S *ristretto.Scalar
}
// DiscreteLogEquivalenceProof constructs a valid DLEQProof for the given parameters and transcript
// Given Y = kX & Q = kP
// Given P = kX, Q = kP, Y=kX
// Peggy: t := choose randomly from Zq
// A := tX
// B := tP
@ -22,52 +21,49 @@ type DLEQProof struct {
// 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)
A := new(ristretto.Element).ScalarMult(t, X)
B := new(ristretto.Element).ScalarMult(t, P)
func DiscreteLogEquivalenceProof(k *ristretto.Scalar, X *ristretto.Point, Y *ristretto.Point, P *ristretto.Point, Q *ristretto.Point, transcript *core.Transcript) DLEQProof {
t := new(ristretto.Scalar).Rand()
A := new(ristretto.Point).ScalarMult(X, t)
B := new(ristretto.Point).ScalarMult(P, t)
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))
s := new(ristretto.Scalar).Sub(t, new(ristretto.Scalar).Mul(c, k))
return DLEQProof{c, s}
}
// VerifyDiscreteLogEquivalenceProof verifies the DLEQ for the given parameters and transcript
// Given Y = kX & Q = kP and Proof = (c,s)
// Given P = kX, Q = kP, Y=kX, 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
// A' = X'+Y' == sX + cY ?= sX + ckX == (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 {
func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Point, Y *ristretto.Point, P *ristretto.Point, Q *ristretto.Point, transcript *core.Transcript) bool {
Xs := new(ristretto.Element).ScalarMult(dleq.S, X)
Yc := new(ristretto.Element).ScalarMult(dleq.C, Y)
Ps := new(ristretto.Element).ScalarMult(dleq.S, P)
Qc := new(ristretto.Element).ScalarMult(dleq.C, Q)
Xs := new(ristretto.Point).ScalarMult(X, dleq.S)
Yc := new(ristretto.Point).ScalarMult(Y, dleq.C)
Ps := new(ristretto.Point).ScalarMult(P, dleq.S)
Qc := new(ristretto.Point).ScalarMult(Q, dleq.C)
A := new(ristretto.Element).Add(Xs, Yc)
B := new(ristretto.Element).Add(Ps, Qc)
A := new(ristretto.Point).Add(Xs, Yc)
B := new(ristretto.Point).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
return transcript.CommitToTranscriptScalar("c").Equals(dleq.C)
}

View File

@ -6,8 +6,7 @@ import (
"cwtch.im/tapir/primitives/core"
"fmt"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
ristretto "github.com/gtank/ristretto255"
"github.com/bwesterb/go-ristretto"
"golang.org/x/crypto/sha3"
)
@ -16,22 +15,17 @@ import (
type Token struct {
t []byte
r *ristretto.Scalar
W *ristretto.Element
}
// GetT returns the underlying bytes for token for use in constraint proofs.
func (t Token) GetT() []byte {
return t.t
W *ristretto.Point
}
// BlindedToken encapsulates a Blinded Token
type BlindedToken struct {
P *ristretto.Element
P *ristretto.Point
}
// SignedToken encapsulates a Signed (Blinded) Token
type SignedToken struct {
Q *ristretto.Element
Q *ristretto.Point
}
// SpentToken encapsulates the parameters needed to spend a Token
@ -43,8 +37,7 @@ type SpentToken struct {
// TokenPaymentHandler defines an interface with external payment processors
type TokenPaymentHandler interface {
MakePayment()
// Next Token
NextToken(data []byte, hostname string) (SpentToken, error)
NextToken(data []byte) (SpentToken, error)
}
// GenBlindedToken initializes the Token
@ -52,25 +45,23 @@ type TokenPaymentHandler interface {
func (t *Token) GenBlindedToken() BlindedToken {
t.t = make([]byte, 32)
rand.Read(t.t)
t.r = new(ristretto.Scalar)
b := make([]byte, 64)
rand.Read(b)
t.r.FromUniformBytes(b)
t.r = new(ristretto.Scalar).Rand()
Ht := sha3.Sum512(t.t)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
P := new(ristretto.Element).ScalarMult(t.r, T)
Ht := sha3.Sum256(t.t)
log.Debugf("token: %x", Ht)
T := new(ristretto.Point).SetElligator(&Ht)
P := new(ristretto.Point).ScalarMult(T, t.r)
return BlindedToken{P}
}
// unblindSignedToken unblinds a token that has been signed by a server
func (t *Token) unblindSignedToken(token SignedToken) {
t.W = new(ristretto.Element).ScalarMult(new(ristretto.Scalar).Invert(t.r), token.Q)
t.W = new(ristretto.Point).ScalarMult(token.Q, new(ristretto.Scalar).Inverse(t.r))
}
// 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[:])
return SpentToken{t.t, mac.Sum(data)}
}
@ -85,29 +76,28 @@ 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 {
func verifyBatchProof(dleq DLEQProof, Y *ristretto.Point, 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, new(ristretto.Point).SetBase().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 := new(ristretto.Point).SetZero()
Z := new(ristretto.Point).SetZero()
for i := range blindedTokens {
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)
M = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(blindedTokens[i].P, c), M)
Z = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(signedTokens[i].Q, c), Z)
}
return VerifyDiscreteLogEquivalenceProof(dleq, new(ristretto.Element).Base(), Y, M, Z, transcript)
return VerifyDiscreteLogEquivalenceProof(dleq, new(ristretto.Point).SetBase(), 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
// verifies that the signing procedure has taken place correctly and unblinds the tokens.
func UnblindSignedTokenBatch(tokens []*Token, blindedTokens []BlindedToken, signedTokens []SignedToken, Y *ristretto.Element, proof DLEQProof, transcript *core.Transcript) bool {
func UnblindSignedTokenBatch(tokens []*Token, blindedTokens []BlindedToken, signedTokens []SignedToken, Y *ristretto.Point, proof DLEQProof, transcript *core.Transcript) bool {
verified := verifyBatchProof(proof, Y, blindedTokens, signedTokens, transcript)
if !verified {
log.Debugf("Failed to unblind tokens: %v", transcript.OutputTranscriptToAudit())
return false
}
for i, t := range tokens {

View File

@ -4,8 +4,6 @@ import (
"cwtch.im/tapir/persistence"
"cwtch.im/tapir/primitives/core"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"github.com/gtank/ristretto255"
"golang.org/x/crypto/sha3"
"testing"
)
@ -33,41 +31,10 @@ func TestToken_SpendToken(t *testing.T) {
}
}
func TestToken_ConstrainToToken(t *testing.T) {
server := NewTokenServer()
token := new(Token)
blindedToken := token.GenBlindedToken()
signedToken := server.SignBlindedToken(blindedToken)
token.unblindSignedToken(signedToken)
spentToken := token.SpendToken([]byte("Hello"))
if server.SpendToken(spentToken, []byte("Hello World")) == nil {
t.Errorf("Token Should be InValid")
}
token2 := new(Token)
blindedToken2 := token2.GenBlindedToken()
Ht := sha3.Sum512(token.t)
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 := 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.
t.Logf("Result of constaint proof %v", UnblindSignedTokenBatch([]*Token{token2}, []BlindedToken{blindedToken2, {P: T}}, append(signedTokens.SignedTokens, SignedToken{token.W}), server.Y, signedTokens.Proof, transcript))
t.Log(transcript.OutputTranscriptToAudit())
}
func TestGenerateBlindedTokenBatch(t *testing.T) {
log.SetLevel(log.LevelDebug)
db := new(persistence.BoltPersistence)
db.Open("tokens.db")
defer db.Close()
server := NewTokenServerFromStore(db)
clientTranscript := core.NewTranscript("privacyPass")
@ -98,4 +65,5 @@ func TestGenerateBlindedTokenBatch(t *testing.T) {
if verified {
t.Errorf("Something went wrong, the proof passed with wrong transcript: %s", wrongTranscript.OutputTranscriptToAudit())
}
db.Close()
}

View File

@ -2,12 +2,11 @@ package privacypass
import (
"crypto/hmac"
"crypto/rand"
"cwtch.im/tapir/persistence"
"cwtch.im/tapir/primitives/core"
"encoding/hex"
"fmt"
ristretto "github.com/gtank/ristretto255"
"github.com/bwesterb/go-ristretto"
"golang.org/x/crypto/sha3"
"sync"
)
@ -15,7 +14,7 @@ import (
// TokenServer implements a token server.
type TokenServer struct {
k *ristretto.Scalar
Y *ristretto.Element
Y *ristretto.Point
seen map[string]bool
persistanceService persistence.Service
mutex sync.Mutex
@ -23,53 +22,28 @@ type TokenServer struct {
// SignedBatchWithProof encapsulates a signed batch of blinded tokens with a batch proof for verification
type SignedBatchWithProof struct {
SignedTokens []SignedToken `json:"st"`
Proof DLEQProof `json:"dp"`
SignedTokens []SignedToken
Proof DLEQProof
}
const tokenBucket = "tokens"
const keyBucket = "keys"
// NewTokenServer generates a new TokenServer (used mostly for testing with ephemeral instances)
func NewTokenServer() *TokenServer {
k := new(ristretto.Scalar)
b := make([]byte, 64)
_, err := rand.Read(b)
if err != nil {
// unable to generate secure random numbers
panic("unable to generate secure random numbers")
}
k.FromUniformBytes(b)
return &TokenServer{k, new(ristretto.Element).ScalarBaseMult(k), make(map[string]bool), nil, sync.Mutex{}}
func NewTokenServer() TokenServer {
k := new(ristretto.Scalar).Rand()
return TokenServer{k, new(ristretto.Point).ScalarMultBase(k), make(map[string]bool), nil, sync.Mutex{}}
}
// NewTokenServerFromStore generates a new TokenServer backed by a persistence service.
func NewTokenServerFromStore(persistenceService persistence.Service) *TokenServer {
tokenServer := NewTokenServer()
func NewTokenServerFromStore(persistenceService persistence.Service) TokenServer {
k := new(ristretto.Scalar).Rand()
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)
}
tokenServer.persistanceService = persistenceService
return tokenServer
return TokenServer{k, new(ristretto.Point).ScalarMultBase(k), make(map[string]bool), persistenceService, sync.Mutex{}}
}
// SignBlindedToken calculates kP for the given BlindedToken P
func (ts *TokenServer) SignBlindedToken(bt BlindedToken) SignedToken {
Q := new(ristretto.Element).ScalarMult(ts.k, bt.P)
Q := new(ristretto.Point).ScalarMult(bt.P, ts.k)
return SignedToken{Q}
}
@ -82,39 +56,24 @@ func (ts *TokenServer) SignBlindedTokenBatch(blindedTokens []BlindedToken, trans
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 {
var signedTokens []SignedToken
for _, bt := range blindedTokens {
signedTokens = append(signedTokens, ts.SignBlindedToken(bt))
}
Ht := sha3.Sum512(constraintToken)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
// 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)}
}
// 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 {
transcript.NewProtocol(BatchProofProtocol)
transcript.AddToTranscript(BatchProofX, new(ristretto.Element).Base().Encode(nil))
transcript.AddToTranscript(BatchProofY, ts.Y.Encode(nil))
transcript.AddToTranscript(BatchProofX, new(ristretto.Point).SetBase().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 := new(ristretto.Point).SetZero()
Z := new(ristretto.Point).SetZero()
for i := range blindedTokens {
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)
M = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(blindedTokens[i].P, c), M)
Z = new(ristretto.Point).Add(new(ristretto.Point).ScalarMult(signedTokens[i].Q, c), Z)
}
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Element).Base(), ts.Y, M, Z, transcript)
return DiscreteLogEquivalenceProof(ts.k, new(ristretto.Point).SetBase(), ts.Y, M, Z, transcript)
}
// SpendToken returns true a SpentToken is valid and has never been spent before, false otherwise.
@ -131,13 +90,13 @@ func (ts *TokenServer) SpendToken(token SpentToken, data []byte) error {
return fmt.Errorf("token: %v has already been spent", token)
}
}
Ht := sha3.Sum512(token.T)
T := new(ristretto.Element).FromUniformBytes(Ht[:])
W := new(ristretto.Element).ScalarMult(ts.k, T)
key := sha3.Sum256(append(token.T, W.Encode(nil)...))
Ht := sha3.Sum256(token.T)
T := new(ristretto.Point).SetElligator(&Ht)
W := new(ristretto.Point).ScalarMult(T, ts.k)
key := sha3.Sum256(append(token.T, W.Bytes()...))
mac := hmac.New(sha3.New512, key[:])
computedMAC := mac.Sum(data)
result := hmac.Equal(token.MAC, computedMAC)
K := mac.Sum(data)
result := hmac.Equal(token.MAC, K)
if result == true {
if ts.persistanceService == nil {
ts.seen[hex.EncodeToString(token.T)] = true

View File

@ -9,6 +9,7 @@ import (
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/nacl/secretbox"
"io"
"net"
"sync"
)
@ -42,7 +43,7 @@ type Connection interface {
// Connection defines a Tapir Connection
type connection struct {
hostname string
conn io.ReadWriteCloser
conn net.Conn
capabilities sync.Map
encrypted bool
key [32]byte
@ -51,11 +52,10 @@ type connection struct {
outbound bool
closed bool
MaxLength int
lock sync.Mutex
}
// NewConnection creates a new Connection
func NewConnection(id *primitives.Identity, hostname string, outbound bool, conn io.ReadWriteCloser, app Application) Connection {
func NewConnection(id *primitives.Identity, hostname string, outbound bool, conn net.Conn, app Application) Connection {
connection := new(connection)
connection.hostname = hostname
connection.conn = conn
@ -74,23 +74,17 @@ func (c *connection) ID() *primitives.Identity {
// App returns the overarching application using this Connection.
func (c *connection) App() Application {
c.lock.Lock()
defer c.lock.Unlock()
return c.app
}
// App returns the overarching application using this Connection.
func (c *connection) SetApp(application Application) {
c.lock.Lock()
defer c.lock.Unlock()
c.app = application
}
// Hostname returns the hostname of the connection (if the connection has not been authorized it will return the
// temporary hostname identifier)
func (c *connection) Hostname() string {
c.lock.Lock()
defer c.lock.Unlock()
return c.hostname
}
@ -102,15 +96,11 @@ func (c *connection) IsOutbound() bool {
// IsClosed returns true if the connection is closed (connections cannot be reopened)
func (c *connection) IsClosed() bool {
c.lock.Lock()
defer c.lock.Unlock()
return c.closed
}
// SetHostname sets the hostname on the connection
func (c *connection) SetHostname(hostname string) {
c.lock.Lock()
defer c.lock.Unlock()
log.Debugf("[%v -- %v] Asserting Remote Hostname: %v", c.identity.Hostname(), c.hostname, hostname)
c.hostname = hostname
}
@ -129,8 +119,6 @@ func (c *connection) HasCapability(name Capability) bool {
// Close forcibly closes the connection
func (c *connection) Close() {
c.lock.Lock()
defer c.lock.Unlock()
c.closed = true
c.conn.Close()
}
@ -146,8 +134,7 @@ func (c *connection) Expect() []byte {
c.closed = true
return []byte{}
}
c.lock.Lock()
defer c.lock.Unlock()
if c.encrypted {
var decryptNonce [24]byte
copy(decryptNonce[:], buffer[:24])
@ -161,18 +148,13 @@ func (c *connection) Expect() []byte {
return []byte{}
}
}
length, _ := binary.Uvarint(buffer[0:2])
if length+2 >= uint64(c.MaxLength) {
return []byte{}
}
len, _ := binary.Uvarint(buffer[0:2])
//cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer)
return buffer[2 : length+2]
return buffer[2 : len+2]
}
// SetEncryptionKey turns on application-level encryption on the connection using the given key.
func (c *connection) SetEncryptionKey(key [32]byte) {
c.lock.Lock()
defer c.lock.Unlock()
c.key = key
c.encrypted = true
}
@ -184,8 +166,6 @@ func (c *connection) Send(message []byte) {
binary.PutUvarint(buffer[0:2], uint64(len(message)))
copy(buffer[2:], message)
c.lock.Lock()
defer c.lock.Unlock()
if c.encrypted {
var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {

View File

@ -2,12 +2,12 @@
set -e
pwd
go test -race ${1} -coverprofile=applications.cover.out -v ./applications
go test -race ${1} -coverprofile=applications.tokenboard.cover.out -v ./applications/tokenboard
go test -race ${1} -coverprofile=primitives.cover.out -v ./primitives
go test -race ${1} -coverprofile=primitives.auditable.cover.out -v ./primitives/auditable
go test -race ${1} -coverprofile=primitives.core.cover.out -v ./primitives/core
go test -race ${1} -coverprofile=primitives.privacypass.cover.out -v ./primitives/privacypass
go test ${1} -coverprofile=applications.cover.out -v ./applications
go test ${1} -coverprofile=applications.tokenboard.cover.out -v ./applications/tokenboard
go test ${1} -coverprofile=primitives.cover.out -v ./primitives
go test ${1} -coverprofile=primitives.auditable.cover.out -v ./primitives/auditable
go test ${1} -coverprofile=primitives.core.cover.out -v ./primitives/core
go test ${1} -coverprofile=primitives.privacypass.cover.out -v ./primitives/privacypass
go test -bench "BenchmarkAuditableStore" -benchtime 1000x primitives/auditable/*.go
echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \
awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out